View Javadoc

1   /* $Id: GStringTemplateEngine.java,v 1.10 2005/07/16 20:00:25 phk 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.*;
48  
49  import java.io.IOException;
50  import java.io.PrintWriter;
51  import java.io.Reader;
52  import java.io.StringWriter;
53  import java.io.Writer;
54  import java.security.AccessController;
55  import java.security.PrivilegedAction;
56  import java.util.Map;
57  
58  import org.codehaus.groovy.control.CompilationFailedException;
59  
60  
61  /***
62  * @author tug@wilson.co.uk
63  *
64  */
65  public class GStringTemplateEngine extends TemplateEngine {
66      /* (non-Javadoc)
67       * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
68       */
69      public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
70          return new GStringTemplate(reader);
71      }
72  
73      private static class GStringTemplate implements Template {
74          final Closure template;
75  
76          /***
77           * Turn the template into a writable Closure
78           * When executed the closure evaluates all the code embedded in the
79           * template and then writes a GString containing the fixed and variable items
80           * to the writer passed as a paramater
81           *
82           * For example:
83           *
84           * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
85           *
86           * would compile into:
87           *
88           * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
89           *
90           * @param reader
91           * @throws CompilationFailedException
92           * @throws ClassNotFoundException
93           * @throws IOException
94           */
95          public GStringTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
96              final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\n def getTemplate() { return { out -> out << \"\"\"");
97              boolean writingString = true;
98         
99              while(true) {
100                 int c = reader.read();
101 
102                     if (c == -1) break;
103 
104                 if (c == '<') {
105                     c = reader.read();
106 
107                     if (c == '%') {
108                         c = reader.read();
109 
110                         if (c == '=') {
111                                 parseExpression(reader, writingString, templateExpressions);
112                                 writingString = true;
113                                 continue;
114                         } else {
115                                 parseSection(reader, writingString, templateExpressions);
116                                 writingString = false;
117                                 continue;
118                         }
119                     } else {
120                         appendCharacter('<', templateExpressions, writingString);
121                         writingString = true;
122                     }
123                 } else if (c == '"') {
124                         appendCharacter('//', templateExpressions, writingString);
125                         writingString = true;
126                    }
127 
128                     appendCharacter((char)c, templateExpressions, writingString);
129                     writingString = true;
130             }
131 
132             if (writingString) {
133                     templateExpressions.append("\"\"\"");
134             }
135 
136             templateExpressions.append("}.asWritable()}");
137 
138 //            System.out.println(templateExpressions.toString());
139 
140             final ClassLoader parentLoader = getClass().getClassLoader();
141             final GroovyClassLoader loader =
142                 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
143                     public Object run() {
144                         return new GroovyClassLoader(parentLoader);
145                     }
146                 });
147             final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x"));
148 
149             try {
150                 final GroovyObject object = (GroovyObject) groovyClass.newInstance();
151 
152                 this.template = (Closure)object.invokeMethod("getTemplate", null);
153             } catch (InstantiationException e) {
154                 throw new ClassNotFoundException(e.getMessage());
155             } catch (IllegalAccessException e) {
156                 throw new ClassNotFoundException(e.getMessage());
157             }
158         }
159 
160         private static void appendCharacter(final char c,
161                                           final StringBuffer templateExpressions,
162                                           final boolean writingString)
163         {
164             if (!writingString) {
165                 templateExpressions.append("out << \"\"\"");
166             }
167 
168             templateExpressions.append(c);
169         }
170 
171         /***
172          * Parse a <% .... %> section
173          * if we are writing a GString close and append ';'
174          * then write the section as a statement
175          *
176          * @param reader
177          * @param writingString
178          * @param templateExpressions
179          * @throws IOException
180          */
181         private static void parseSection(final Reader reader,
182                                         final boolean writingString,
183                                          final StringBuffer templateExpressions)
184             throws IOException
185         {
186             if (writingString) {
187                 templateExpressions.append("\"\"\"; ");
188             }
189 
190                 while (true) {
191                     int c = reader.read();
192 
193                     if (c == -1) break;
194 
195                     if (c =='%') {
196                         c = reader.read();
197 
198                         if (c == '>') break;
199                     }
200 
201                     templateExpressions.append((char)c);
202                 }
203 
204                 templateExpressions.append("; ");
205         }
206 
207         /***
208          * Parse a <%= .... %> expression
209          *
210          * @param reader
211          * @param writingString
212          * @param templateExpressions
213          * @throws IOException
214          */
215         private static void parseExpression(final Reader reader,
216                                           final boolean writingString,
217                                           final StringBuffer templateExpressions)
218             throws IOException
219         {
220             if (!writingString) {
221                 templateExpressions.append("out << \"\"\"");
222             }
223 
224             templateExpressions.append("${");
225 
226                 while (true) {
227                     int c = reader.read();
228 
229                     if (c == -1) break;
230 
231                     if (c =='%') {
232                         c = reader.read();
233 
234                         if (c == '>') break;
235                     }
236 
237                     templateExpressions.append((char)c);
238                 }
239 
240             templateExpressions.append('}');
241         }
242 
243         public Writable make() {
244            return make(null);
245        }
246 
247        public Writable make(final Map map) {
248        final Closure delegatedClosure = (Closure)this.template.clone();
249            
250            delegatedClosure.setDelegate(new Binding(map));
251            
252            return new Writable() {
253                /* (non-Javadoc)
254                * @see groovy.lang.Writable#writeTo(java.io.Writer)
255                */
256                public Writer writeTo(final Writer writer) throws IOException {
257                    delegatedClosure.call(new ParameterArray(new PrintWriter(writer)));
258 
259                    return writer;
260                }
261 
262                /* (non-Javadoc)
263                * @see java.lang.Object#toString()
264                */
265                public String toString() {
266                    final StringWriter stringWriter = new StringWriter();
267 
268                    try {
269                        writeTo(stringWriter);
270 
271                        return stringWriter.toString();
272                    } catch (final IOException e) {
273                        return e.toString();
274                    }
275                }
276 
277            };
278        }
279     }
280 }