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 }