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