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