1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
67
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\nclass C { getTemplate() { { |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
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
254
255
256 public Writer writeTo(final Writer writer) throws IOException {
257 delegatedClosure.call(new Object[] {new PrintWriter(writer)});
258
259 return writer;
260 }
261
262
263
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 }