View Javadoc

    1   /*
    2    * $Id: SimpleTemplateEngine.java,v 1.17 2005/07/22 09:37:33 cstein Exp $
    3    *
    4    * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
    5    *
    6    * Redistribution and use of this software and associated documentation
    7    * ("Software"), with or without modification, are permitted provided that the
    8    * following conditions are met: 1. Redistributions of source code must retain
    9    * copyright statements and notices. Redistributions must also contain a copy
    10   * of this document. 2. Redistributions in binary form must reproduce the above
    11   * copyright notice, this list of conditions and the following disclaimer in
    12   * the documentation and/or other materials provided with the distribution. 3.
    13   * The name "groovy" must not be used to endorse or promote products derived
    14   * from this Software without prior written permission of The Codehaus. For
    15   * written permission, please contact info@codehaus.org. 4. Products derived
    16   * from this Software may not be called "groovy" nor may "groovy" appear in
    17   * their names without prior written permission of The Codehaus. "groovy" is a
    18   * registered trademark of The Codehaus. 5. Due credit should be given to The
    19   * Codehaus - http://groovy.codehaus.org/
    20   *
    21   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    22   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    23   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    24   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    31   * DAMAGE.
    32   *
    33   */
    34  package groovy.text;
    35
    36  import groovy.lang.Binding;
    37  import groovy.lang.GroovyShell;
    38  import groovy.lang.Script;
    39  import groovy.lang.Writable;
    40
    41  import java.io.BufferedReader;
    42  import java.io.IOException;
    43  import java.io.PrintWriter;
    44  import java.io.Reader;
    45  import java.io.StringWriter;
    46  import java.io.Writer;
    47  import java.util.Map;
    48
    49  import org.codehaus.groovy.control.CompilationFailedException;
    50  import org.codehaus.groovy.runtime.InvokerHelper;
    51
    52  /***
    53   * This simple template engine uses JSP <% %> script and <%= %> expression syntax.  It also lets you use normal groovy expressions in
    54   * the template text much like the new JSP EL functionality.  The variable 'out' is bound to the writer that the template is being written to.
    55   *
    56   * @author sam
    57   * @author Christian Stein
    58   */
    59  public class SimpleTemplateEngine extends TemplateEngine {
    60
    61      private final boolean verbose;
    62
    63      public SimpleTemplateEngine() {
    64          this(false);
    65      }
    66
    67      public SimpleTemplateEngine(boolean verbose) {
    68          this.verbose = verbose;
    69      }
    70
    71      public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
    72          SimpleTemplate template = new SimpleTemplate();
    73          GroovyShell shell = new GroovyShell();
    74          String script = template.parse(reader);
    75          if (verbose) {
    76              System.out.println("\n-- script source --");
    77              System.out.print(script);
    78              System.out.println("\n-- script end --\n");
    79          }
    80          template.script = shell.parse(script);
    81          return template;
    82      }
    83
    84      private static class SimpleTemplate implements Template {
    85
    86          protected Script script;
    87
    88          public Writable make() {
    89              return make(null);
    90          }
    91
    92          public Writable make(final Map map) {
    93              return new Writable() {
    94                  /***
    95                   * Write the template document with the set binding applied to the writer.
    96                   *
    97                   * @see groovy.lang.Writable#writeTo(java.io.Writer)
    98                   */
    99                  public Writer writeTo(Writer writer) {
    100                     Binding binding;
    101                     if (map == null)
    102                         binding = new Binding();
    103                     else
    104                         binding = new Binding(map);
    105                     Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
    106                     PrintWriter pw = new PrintWriter(writer);
    107                     scriptObject.setProperty("out", pw);
    108                     scriptObject.run();
    109                     pw.flush();
    110                     return writer;
    111                 }
    112
    113                 /***
    114                  * Convert the template and binding into a result String.
    115                  *
    116                  * @see java.lang.Object#toString()
    117                  */
    118                 public String toString() {
    119                     try {
    120                         StringWriter sw = new StringWriter();
    121                         writeTo(sw);
    122                         return sw.toString();
    123                     } catch (Exception e) {
    124                         return e.toString();
    125                     }
    126                 }
    127             };
    128         }
    129
    130         /***
    131          * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly
    132          * into the script while escaping quotes.
    133          *
    134          * @param reader
    135          * @return
    136          * @throws IOException
    137          */
    138         protected String parse(Reader reader) throws IOException {
    139             if (!reader.markSupported()) {
    140                 reader = new BufferedReader(reader);
    141             }
    142             StringWriter sw = new StringWriter();
    143             startScript(sw);
    144             boolean start = false;
    145             int c;
    146             while ((c = reader.read()) != -1) {
    147                 if (c == '<') {
    148                     reader.mark(1);
    149                     c = reader.read();
    150                     if (c != '%') {
    151                         sw.write('<');
    152                         reader.reset();
    153                     } else {
    154                         reader.mark(1);
    155                         c = reader.read();
    156                         if (c == '=') {
    157                             groovyExpression(reader, sw);
    158                         } else {
    159                             reader.reset();
    160                             groovySection(reader, sw);
    161                         }
    162                     }
    163                     continue; // at least '<' is consumed ... read next chars.
    164                 }
    165                 if (c == '\"') {
    166                     sw.write('//');
    167                 }
    168                 /*
    169                  * Handle raw new line characters.
    170                  */
    171                 if (c == '\n' || c == '\r') {
    172                     if (c == '\r') { // on Windows, "\r\n" is a new line.
    173                         reader.mark(1);
    174                         c = reader.read();
    175                         if (c != '\n') {
    176                             reader.reset();
    177                         }
    178                     }
    179                     sw.write("//n\");\nout.print(\"");
    180                     continue;
    181                 }
    182                 sw.write(c);
    183             }
    184             endScript(sw);
    185             String result = sw.toString();
    186             return result;
    187         }
    188
    189         private void startScript(StringWriter sw) {
    190             sw.write("/* Generated by SimpleTemplateEngine */\n");
    191             sw.write("out.print(\"");
    192         }
    193
    194         private void endScript(StringWriter sw) {
    195             sw.write("\");\n");
    196         }
    197
    198         /***
    199          * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
    200          *
    201          * @param reader
    202          * @param sw
    203          * @throws IOException
    204          */
    205         private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
    206             sw.write("\");out.print(\"${");
    207             int c;
    208             while ((c = reader.read()) != -1) {
    209                 if (c == '%') {
    210                     c = reader.read();
    211                     if (c != '>') {
    212                         sw.write('%');
    213                     } else {
    214                         break;
    215                     }
    216                 }
    217                 if (c != '\n' && c != '\r') {
    218                     sw.write(c);
    219                 }
    220             }
    221             sw.write("}\");\nout.print(\"");
    222         }
    223
    224         /***
    225          * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
    226          *
    227          * @param reader
    228          * @param sw
    229          * @throws IOException
    230          */
    231         private void groovySection(Reader reader, StringWriter sw) throws IOException {
    232             sw.write("\");");
    233             int c;
    234             while ((c = reader.read()) != -1) {
    235                 if (c == '%') {
    236                     c = reader.read();
    237                     if (c != '>') {
    238                         sw.write('%');
    239                     } else {
    240                         break;
    241                     }
    242                 }
    243                 /* Don't eat EOL chars in sections - as they are valid instruction separators.
    244                  * See http://jira.codehaus.org/browse/GROOVY-980
    245                  */
    246                 // if (c != '\n' && c != '\r') {
    247                 sw.write(c);
    248                 //}
    249             }
    250             sw.write(";\nout.print(\"");
    251         }
    252
    253     }
    254 }