View Javadoc

    1   /* $Id: GStringTemplateEngine.java,v 1.17 2006/01/12 10:00:22 tug Exp $
    2
    3   Copyright 2004 (C) John Wilson. All Rights Reserved.
    4
    5   Redistribution and use of this software and associated documentation
    6   ("Software"), with or without modification, are permitted provided
    7   that the following conditions are met:
    8
    9   1. Redistributions of source code must retain copyright
    10     statements and notices.  Redistributions must also contain a
    11     copy of this document.
    12
    13  2. Redistributions in binary form must reproduce the
    14     above copyright notice, this list of conditions and the
    15     following disclaimer in the documentation and/or other
    16     materials provided with the distribution.
    17
    18  3. The name "groovy" must not be used to endorse or promote
    19     products derived from this Software without prior written
    20     permission of The Codehaus.  For written permission,
    21     please contact info@codehaus.org.
    22
    23  4. Products derived from this Software may not be called "groovy"
    24     nor may "groovy" appear in their names without prior written
    25     permission of The Codehaus. "groovy" is a registered
    26     trademark of The Codehaus.
    27
    28  5. Due credit should be given to The Codehaus -
    29     http://groovy.codehaus.org/
    30
    31  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    32  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    33  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    34  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    35  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    36  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    37  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    38  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    39  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    40  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    41  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    42  OF THE POSSIBILITY OF SUCH DAMAGE.
    43
    44  */
    45  package groovy.text;
    46
    47  import groovy.lang.Closure;
    48  import groovy.lang.GroovyClassLoader;
    49  import groovy.lang.GroovyCodeSource;
    50  import groovy.lang.GroovyObject;
    51  import groovy.lang.Writable;
    52
    53  import java.io.IOException;
    54  import java.io.Reader;
    55  import java.security.AccessController;
    56  import java.security.PrivilegedAction;
    57  import java.util.Map;
    58
    59  import org.codehaus.groovy.control.CompilationFailedException;
    60
    61
    62  /***
    63  * @author tug@wilson.co.uk
    64  *
    65  */
    66  public class GStringTemplateEngine extends TemplateEngine {
    67      /* (non-Javadoc)
    68       * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
    69       */
    70      public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
    71          return new GStringTemplate(reader);
    72      }
    73
    74      private static class GStringTemplate implements Template {
    75          final Closure template;
    76
    77          /***
    78           * Turn the template into a writable Closure
    79           * When executed the closure evaluates all the code embedded in the
    80           * template and then writes a GString containing the fixed and variable items
    81           * to the writer passed as a paramater
    82           *
    83           * For example:
    84           *
    85           * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
    86           *
    87           * would compile into:
    88           *
    89           * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
    90           *
    91           * @param reader
    92           * @throws CompilationFailedException
    93           * @throws ClassNotFoundException
    94           * @throws IOException
    95           */
    96          public GStringTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
    97          final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\n def getTemplate() { return { out -> delegate = new Binding(delegate); out << \"\"\"");
    98          boolean writingString = true;
    99
    100             while(true) {
    101                 int c = reader.read();
    102
    103                     if (c == -1) break;
    104
    105                 if (c == '<') {
    106                     c = reader.read();
    107
    108                     if (c == '%') {
    109                         c = reader.read();
    110
    111                         if (c == '=') {
    112                                 parseExpression(reader, writingString, templateExpressions);
    113                                 writingString = true;
    114                                 continue;
    115                         } else {
    116                                 parseSection(c, reader, writingString, templateExpressions);
    117                                 writingString = false;
    118                                 continue;
    119                         }
    120                     } else {
    121                         appendCharacter('<', templateExpressions, writingString);
    122                         writingString = true;
    123                     }
    124                 } else if (c == '"') {
    125                         appendCharacter('//', templateExpressions, writingString);
    126                         writingString = true;
    127                    }
    128
    129                     appendCharacter((char)c, templateExpressions, writingString);
    130                     writingString = true;
    131             }
    132
    133             if (writingString) {
    134                     templateExpressions.append("\"\"\"");
    135             }
    136
    137             templateExpressions.append("}.asWritable()}");
    138
    139 //            System.out.println(templateExpressions.toString());
    140
    141             final ClassLoader parentLoader = getClass().getClassLoader();
    142             final GroovyClassLoader loader =
    143                 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
    144                     public Object run() {
    145                         return new GroovyClassLoader(parentLoader);
    146                     }
    147                 });
    148             final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x"));
    149
    150             try {
    151                 final GroovyObject object = (GroovyObject) groovyClass.newInstance();
    152
    153                 this.template = (Closure)object.invokeMethod("getTemplate", null);
    154             } catch (InstantiationException e) {
    155                 throw new ClassNotFoundException(e.getMessage());
    156             } catch (IllegalAccessException e) {
    157                 throw new ClassNotFoundException(e.getMessage());
    158             }
    159         }
    160
    161         private static void appendCharacter(final char c,
    162                                             final StringBuffer templateExpressions,
    163                                             final boolean writingString)
    164         {
    165             if (!writingString) {
    166                 templateExpressions.append("out << \"\"\"");
    167             }
    168
    169             templateExpressions.append(c);
    170         }
    171
    172         /***
    173          * Parse a <% .... %> section
    174          * if we are writing a GString close and append ';'
    175          * then write the section as a statement
    176          *
    177          * @param pendingC
    178          * @param reader
    179          * @param writingString
    180          * @param templateExpressions
    181          * @throws IOException
    182          */
    183         private static void parseSection(final int pendingC,
    184                                          final Reader reader,
    185                                          final boolean writingString,
    186                                          final StringBuffer templateExpressions)
    187             throws IOException
    188         {
    189             if (writingString) {
    190                 templateExpressions.append("\"\"\"; ");
    191             }
    192             templateExpressions.append((char)pendingC);
    193
    194                 while (true) {
    195                     int c = reader.read();
    196
    197                     if (c == -1) break;
    198
    199                     if (c =='%') {
    200                         c = reader.read();
    201
    202                         if (c == '>') break;
    203
    204                         templateExpressions.append('%');
    205                     }
    206
    207                     templateExpressions.append((char)c);
    208                 }
    209
    210                 templateExpressions.append(";\n ");
    211         }
    212
    213         /***
    214          * Parse a <%= .... %> expression
    215          *
    216          * @param reader
    217          * @param writingString
    218          * @param templateExpressions
    219          * @throws IOException
    220          */
    221         private static void parseExpression(final Reader reader,
    222                                           final boolean writingString,
    223                                           final StringBuffer templateExpressions)
    224             throws IOException
    225         {
    226             if (!writingString) {
    227                 templateExpressions.append("out << \"\"\"");
    228             }
    229
    230             templateExpressions.append("${");
    231
    232                 while (true) {
    233                     int c = reader.read();
    234
    235                     if (c == -1) break;
    236
    237                     if (c =='%') {
    238                         c = reader.read();
    239
    240                         if (c == '>') break;
    241
    242                         templateExpressions.append('%');
    243                     }
    244
    245                     templateExpressions.append((char)c);
    246                 }
    247
    248             templateExpressions.append('}');
    249         }
    250
    251         public Writable make() {
    252            return make(null);
    253        }
    254
    255        public Writable make(final Map map) {
    256        final Closure template = (Closure)this.template.clone();
    257
    258            template.setDelegate(map);
    259
    260            return (Writable)template;
    261        }
    262     }
    263 }