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