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 }