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