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