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