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