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 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 * @throws IOException
136 */
137 protected String parse(Reader reader) throws IOException {
138 if (!reader.markSupported()) {
139 reader = new BufferedReader(reader);
140 }
141 StringWriter sw = new StringWriter();
142 startScript(sw);
143 boolean start = false;
144 int c;
145 while ((c = reader.read()) != -1) {
146 if (c == '<') {
147 reader.mark(1);
148 c = reader.read();
149 if (c != '%') {
150 sw.write('<');
151 reader.reset();
152 } else {
153 reader.mark(1);
154 c = reader.read();
155 if (c == '=') {
156 groovyExpression(reader, sw);
157 } else {
158 reader.reset();
159 groovySection(reader, sw);
160 }
161 }
162 continue;
163 }
164 if (c == '\"') {
165 sw.write('//');
166 }
167
168
169
170 if (c == '\n' || c == '\r') {
171 if (c == '\r') {
172 reader.mark(1);
173 c = reader.read();
174 if (c != '\n') {
175 reader.reset();
176 }
177 }
178 sw.write("//n\");\nout.print(\"");
179 continue;
180 }
181 sw.write(c);
182 }
183 endScript(sw);
184 String result = sw.toString();
185 return result;
186 }
187
188 private void startScript(StringWriter sw) {
189 sw.write("/* Generated by SimpleTemplateEngine */\n");
190 sw.write("out.print(\"");
191 }
192
193 private void endScript(StringWriter sw) {
194 sw.write("\");\n");
195 }
196
197 /***
198 * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
199 *
200 * @param reader
201 * @param sw
202 * @throws IOException
203 */
204 private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
205 sw.write("\");out.print(\"${");
206 int c;
207 while ((c = reader.read()) != -1) {
208 if (c == '%') {
209 c = reader.read();
210 if (c != '>') {
211 sw.write('%');
212 } else {
213 break;
214 }
215 }
216 if (c != '\n' && c != '\r') {
217 sw.write(c);
218 }
219 }
220 sw.write("}\");\nout.print(\"");
221 }
222
223 /***
224 * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
225 *
226 * @param reader
227 * @param sw
228 * @throws IOException
229 */
230 private void groovySection(Reader reader, StringWriter sw) throws IOException {
231 sw.write("\");");
232 int c;
233 while ((c = reader.read()) != -1) {
234 if (c == '%') {
235 c = reader.read();
236 if (c != '>') {
237 sw.write('%');
238 } else {
239 break;
240 }
241 }
242
243
244
245
246 sw.write(c);
247
248 }
249 sw.write(";\nout.print(\"");
250 }
251
252 }
253 }