1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal;
18
19 import java.io.Reader;
20 import java.io.Writer;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Set;
26
27 import org.apache.commons.jexl3.JexlContext;
28 import org.apache.commons.jexl3.JexlEngine;
29 import org.apache.commons.jexl3.JexlException;
30 import org.apache.commons.jexl3.JexlInfo;
31 import org.apache.commons.jexl3.JexlOptions;
32 import org.apache.commons.jexl3.JxltEngine;
33 import org.apache.commons.jexl3.internal.TemplateEngine.Block;
34 import org.apache.commons.jexl3.internal.TemplateEngine.BlockType;
35 import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression;
36 import org.apache.commons.jexl3.parser.ASTArguments;
37 import org.apache.commons.jexl3.parser.ASTFunctionNode;
38 import org.apache.commons.jexl3.parser.ASTIdentifier;
39 import org.apache.commons.jexl3.parser.ASTJexlScript;
40 import org.apache.commons.jexl3.parser.ASTNumberLiteral;
41 import org.apache.commons.jexl3.parser.JexlNode;
42
43
44
45
46 public final class TemplateScript implements JxltEngine.Template {
47
48
49
50
51
52
53
54
55
56 private static void collectPrintScope(final JexlNode node, final JexlNode.Info[] callSites) {
57 final int nc = node.jjtGetNumChildren();
58 if (node instanceof ASTFunctionNode && nc == 2) {
59
60 final ASTIdentifier nameNode = (ASTIdentifier) node.jjtGetChild(0);
61 if ("print".equals(nameNode.getName()) && "jexl".equals(nameNode.getNamespace())) {
62
63 final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
64 if (argNode.jjtGetNumChildren() == 1) {
65
66 final JexlNode arg0 = argNode.jjtGetChild(0);
67 if (arg0 instanceof ASTNumberLiteral) {
68 final int exprNumber = ((ASTNumberLiteral) arg0).getLiteral().intValue();
69 callSites[exprNumber] = new JexlNode.Info(nameNode);
70 return;
71 }
72 }
73 }
74 }
75 for (int c = 0; c < nc; ++c) {
76 collectPrintScope(node.jjtGetChild(c), callSites);
77 }
78 }
79
80
81
82
83
84
85
86
87 private static Scope scopeOf(final JexlNode.Info info, final Scope scope) {
88 Scope found = null;
89 JexlNode walk = info.getNode();
90 while (walk != null) {
91 if (walk instanceof ASTJexlScript) {
92 found = ((ASTJexlScript) walk).getScope();
93 break;
94 }
95 walk = walk.jjtGetParent();
96 }
97 return found != null ? found : scope;
98 }
99
100
101
102
103
104
105
106
107 private TemplateExpression[] calleeScripts(final Scope scope, final Block[] blocks, final JexlNode.Info[] callSites) {
108 final TemplateExpression[] expressions = new TemplateExpression[callSites.length];
109
110 int jpe = 0;
111
112 for (final Block block : blocks) {
113 if (block.getType() == BlockType.VERBATIM) {
114 final JexlNode.Info ji = callSites[jpe];
115
116
117 final TemplateExpression te = ji != null
118 ? jxlt.parseExpression(ji, block.getBody(), scopeOf(ji, scope))
119 : jxlt.new ConstantExpression(block.getBody(), null);
120 expressions[jpe++] = te;
121 }
122 }
123 return expressions;
124 }
125
126
127
128
129
130
131
132
133
134 private static String callerScript(final Block[] blocks) {
135 final StringBuilder strb = new StringBuilder();
136 int nuexpr = 0;
137 int line = 1;
138 for (final Block block : blocks) {
139 final int bl = block.getLine();
140 while (line < bl) {
141 strb.append("//\n");
142 line += 1;
143 }
144 if (block.getType() == BlockType.VERBATIM) {
145 strb.append("jexl:print(");
146 strb.append(nuexpr++);
147 strb.append(");\n");
148 line += 1;
149 } else {
150 final String body = block.getBody();
151 strb.append(body);
152
153 for (int c = 0; c < body.length(); ++c) {
154 if (body.charAt(c) == '\n') {
155 line += 1;
156 }
157 }
158 }
159 }
160 return strb.toString();
161 }
162
163
164 private final String prefix;
165
166
167 private final Block[] source;
168
169
170 private final ASTJexlScript script;
171
172
173 private final TemplateExpression[] exprs;
174
175
176 private final TemplateEngine jxlt;
177
178
179
180
181
182
183
184
185
186
187
188
189
190 public TemplateScript(final TemplateEngine engine,
191 final JexlInfo jexlInfo,
192 final String directive,
193 final Reader reader,
194 final String... parms) {
195 Objects.requireNonNull(directive, "directive");
196 final String engineImmediateCharString = Character.toString(engine.getImmediateChar());
197 final String engineDeferredCharString = Character.toString(engine.getDeferredChar());
198
199 if (engineImmediateCharString.equals(directive)
200 || engineDeferredCharString.equals(directive)
201 || (engineImmediateCharString + "{").equals(directive)
202 || (engineDeferredCharString + "{").equals(directive)) {
203 throw new IllegalArgumentException(directive + ": is not a valid directive pattern");
204 }
205 Objects.requireNonNull(reader, "reader");
206 this.jxlt = engine;
207 this.prefix = directive;
208 final Engine jexl = jxlt.getEngine();
209
210 final Block[] blocks = jxlt.readTemplate(prefix, reader).toArray(new Block[0]);
211 int verbatims = 0;
212 for(final Block b : blocks) {
213 if (BlockType.VERBATIM == b.getType()) {
214 verbatims += 1;
215 }
216 }
217 final JexlEngine je = Engine.putThreadEngine(jexl);
218 try {
219 final String scriptSource = callerScript(blocks);
220
221 final JexlInfo info = jexlInfo == null ? jexl.createInfo() : jexlInfo;
222 final Scope scope = parms == null ? null : new Scope(null, parms);
223 final JexlInfo templateInfo = jexl.scriptFeatures.isIgnoreTemplatePrefix()
224 ? new TemplateInfo(info.at(1, 1), Collections.singleton(prefix))
225 : info.at(1, 1);
226 final ASTJexlScript callerScript = jexl.jxltParse(templateInfo, false, scriptSource, scope).script();
227
228
229 final JexlNode.Info[] callSites = new JexlNode.Info[verbatims];
230 collectPrintScope(callerScript.script(), callSites);
231
232 this.exprs = calleeScripts(scope, blocks, callSites);
233 this.script = callerScript;
234 this.source = blocks;
235 } finally {
236 Engine.putThreadEngine(je);
237 }
238 }
239
240
241
242
243
244
245
246
247
248
249 TemplateScript(final TemplateEngine engine,
250 final String thePrefix,
251 final Block[] theSource,
252 final ASTJexlScript theScript,
253 final TemplateExpression[] theExprs) {
254 jxlt = engine;
255 prefix = thePrefix;
256 source = theSource;
257 script = theScript;
258 exprs = theExprs;
259 }
260
261 @Override
262 public String asString() {
263 final StringBuilder strb = new StringBuilder();
264 int e = 0;
265 for (final Block block : source) {
266 if (block.getType() == BlockType.DIRECTIVE) {
267 strb.append(prefix);
268 strb.append(block.getBody());
269 } else {
270 exprs[e++].asString(strb);
271 }
272 }
273 return strb.toString();
274 }
275
276 @Override
277 public void evaluate(final JexlContext context, final Writer writer) {
278 evaluate(context, writer, (Object[]) null);
279 }
280
281 @Override
282 public void evaluate(final JexlContext context, final Writer writer, final Object... args) {
283 final Engine jexl = jxlt.getEngine();
284 final JexlOptions options = jexl.evalOptions(script, context);
285 final Frame frame = script.createFrame(args);
286 final TemplateInterpreter.Arguments targs = new TemplateInterpreter
287 .Arguments(jexl)
288 .context(context)
289 .options(options)
290 .frame(frame)
291 .expressions(exprs)
292 .writer(writer);
293 final Interpreter interpreter = jexl.createTemplateInterpreter(targs);
294 interpreter.interpret(script);
295 }
296
297
298
299
300 TemplateExpression[] getExpressions() {
301 return exprs;
302 }
303
304 @Override
305 public String[] getParameters() {
306 return script.getParameters();
307 }
308
309 @Override
310 public Map<String, Object> getPragmas() {
311 return script.getPragmas();
312 }
313
314
315
316
317 ASTJexlScript getScript() {
318 return script;
319 }
320
321 @Override
322 public Set<List<String>> getVariables() {
323 final Engine.VarCollector collector = jxlt.getEngine().varCollector();
324 for (final TemplateExpression expr : exprs) {
325 expr.getVariables(collector);
326 }
327 return collector.collected();
328 }
329
330 @Override
331 public TemplateScript prepare(final JexlContext context) {
332 return prepare(context, (Object[]) null);
333 }
334
335 @Override
336 public TemplateScript prepare(final JexlContext context, final Object... args) {
337 final Engine jexl = jxlt.getEngine();
338 final JexlOptions options = jexl.evalOptions(script, context);
339 final Frame frame = script.createFrame(args);
340 final TemplateInterpreter.Arguments targs = new TemplateInterpreter
341 .Arguments(jxlt.getEngine())
342 .context(context)
343 .options(options)
344 .frame(frame);
345 final Interpreter interpreter = jexl.createTemplateInterpreter(targs);
346 final TemplateExpression[] prepared = new TemplateExpression[exprs.length];
347 for (int e = 0; e < exprs.length; ++e) {
348 try {
349 prepared[e] = exprs[e].prepare(interpreter);
350 } catch (final JexlException xjexl) {
351 final JexlException xuel = TemplateEngine.createException(xjexl.getInfo(), "prepare", exprs[e], xjexl);
352 if (jexl.isSilent()) {
353 if (jexl.logger.isWarnEnabled()) {
354 jexl.logger.warn(xuel.getMessage(), xuel.getCause());
355 }
356 return null;
357 }
358 throw xuel;
359 }
360 }
361 return new TemplateScript(jxlt, prefix, source, script, prepared);
362 }
363
364 @Override
365 public String toString() {
366 final StringBuilder strb = new StringBuilder();
367 for (final Block block : source) {
368 block.toString(strb, prefix);
369 }
370 return strb.toString();
371 }
372 }