View Javadoc

    1   /*
    2    * $Id: SimpleTemplateEngine.java 4032 2006-08-30 07:18:49Z mguillem $
    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          * @throws IOException
    136          */
    137         protected String parse(Reader reader) throws IOException {
    138             if (!reader.markSupported()) {
    139                 reader = new BufferedReader(reader);
    140             }
    141             StringWriter sw = new StringWriter();
    142             startScript(sw);
    143             boolean start = false;
    144             int c;
    145             while ((c = reader.read()) != -1) {
    146                 if (c == '<') {
    147                     reader.mark(1);
    148                     c = reader.read();
    149                     if (c != '%') {
    150                         sw.write('<');
    151                         reader.reset();
    152                     } else {
    153                         reader.mark(1);
    154                         c = reader.read();
    155                         if (c == '=') {
    156                             groovyExpression(reader, sw);
    157                         } else {
    158                             reader.reset();
    159                             groovySection(reader, sw);
    160                         }
    161                     }
    162                     continue; // at least '<' is consumed ... read next chars.
    163                 }
    164                 if (c == '\"') {
    165                     sw.write('//');
    166                 }
    167                 /*
    168                  * Handle raw new line characters.
    169                  */
    170                 if (c == '\n' || c == '\r') {
    171                     if (c == '\r') { // on Windows, "\r\n" is a new line.
    172                         reader.mark(1);
    173                         c = reader.read();
    174                         if (c != '\n') {
    175                             reader.reset();
    176                         }
    177                     }
    178                     sw.write("//n\");\nout.print(\"");
    179                     continue;
    180                 }
    181                 sw.write(c);
    182             }
    183             endScript(sw);
    184             String result = sw.toString();
    185             return result;
    186         }
    187
    188         private void startScript(StringWriter sw) {
    189             sw.write("/* Generated by SimpleTemplateEngine */\n");
    190             sw.write("out.print(\"");
    191         }
    192
    193         private void endScript(StringWriter sw) {
    194             sw.write("\");\n");
    195         }
    196
    197         /***
    198          * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
    199          *
    200          * @param reader
    201          * @param sw
    202          * @throws IOException
    203          */
    204         private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
    205             sw.write("\");out.print(\"${");
    206             int c;
    207             while ((c = reader.read()) != -1) {
    208                 if (c == '%') {
    209                     c = reader.read();
    210                     if (c != '>') {
    211                         sw.write('%');
    212                     } else {
    213                         break;
    214                     }
    215                 }
    216                 if (c != '\n' && c != '\r') {
    217                     sw.write(c);
    218                 }
    219             }
    220             sw.write("}\");\nout.print(\"");
    221         }
    222
    223         /***
    224          * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
    225          *
    226          * @param reader
    227          * @param sw
    228          * @throws IOException
    229          */
    230         private void groovySection(Reader reader, StringWriter sw) throws IOException {
    231             sw.write("\");");
    232             int c;
    233             while ((c = reader.read()) != -1) {
    234                 if (c == '%') {
    235                     c = reader.read();
    236                     if (c != '>') {
    237                         sw.write('%');
    238                     } else {
    239                         break;
    240                     }
    241                 }
    242                 /* Don't eat EOL chars in sections - as they are valid instruction separators.
    243                  * See http://jira.codehaus.org/browse/GROOVY-980
    244                  */
    245                 // if (c != '\n' && c != '\r') {
    246                 sw.write(c);
    247                 //}
    248             }
    249             sw.write(";\nout.print(\"");
    250         }
    251
    252     }
    253 }