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