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.JexlOptions;
22 import org.apache.commons.jexl3.JxltEngine;
23 import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression;
24 import org.apache.commons.jexl3.introspection.JexlMethod;
25 import org.apache.commons.jexl3.introspection.JexlUberspect;
26 import org.apache.commons.jexl3.parser.ASTArguments;
27 import org.apache.commons.jexl3.parser.ASTFunctionNode;
28 import org.apache.commons.jexl3.parser.ASTIdentifier;
29 import org.apache.commons.jexl3.parser.ASTJexlLambda;
30 import org.apache.commons.jexl3.parser.ASTJexlScript;
31 import org.apache.commons.jexl3.parser.JexlNode;
32
33 import java.io.Writer;
34 import java.util.Arrays;
35
36
37
38
39
40
41 public class TemplateInterpreter extends Interpreter {
42
43 final TemplateExpression[] exprs;
44
45 final Writer writer;
46
47
48
49
50
51 static class Arguments {
52
53 Engine jexl;
54
55 JexlOptions options;
56
57 JexlContext jcontext;
58
59 Frame jframe;
60
61 TemplateExpression[] expressions;
62
63 Writer out;
64
65
66
67
68
69 Arguments(final Engine e) {
70 this.jexl = e;
71 }
72
73
74
75
76
77 Arguments options(final JexlOptions o) {
78 this.options = o;
79 return this;
80 }
81
82
83
84
85
86 Arguments context(final JexlContext j) {
87 this.jcontext = j;
88 return this;
89 }
90
91
92
93
94
95 Arguments frame(final Frame f) {
96 this.jframe = f;
97 return this;
98 }
99
100
101
102
103
104 Arguments expressions(final TemplateExpression[] e) {
105 this.expressions = e;
106 return this;
107 }
108
109
110
111
112
113 Arguments writer(final Writer o) {
114 this.out = o;
115 return this;
116 }
117 }
118
119
120
121
122
123 TemplateInterpreter(final Arguments args) {
124 super(args.jexl, args.options, args.jcontext, args.jframe);
125 exprs = args.expressions;
126 writer = args.out;
127 block = new LexicalFrame(frame, null);
128 }
129
130
131
132
133
134
135
136
137 public void include(final JxltEngine.Template script, final Object... args) {
138 script.evaluate(context, writer, args);
139 }
140
141
142
143
144
145 public void print(final int e) {
146 if (e < 0 || e >= exprs.length) {
147 return;
148 }
149 TemplateEngine.TemplateExpression expr = exprs[e];
150 if (expr.isDeferred()) {
151 expr = expr.prepare(context, frame, options);
152 }
153 if (expr instanceof TemplateEngine.CompositeExpression) {
154 printComposite((TemplateEngine.CompositeExpression) expr);
155 } else {
156 doPrint(expr.getInfo(), expr.evaluate(this));
157 }
158 }
159
160
161
162
163
164 private void printComposite(final TemplateEngine.CompositeExpression composite) {
165 final TemplateEngine.TemplateExpression[] cexprs = composite.exprs;
166 Object value;
167 for (final TemplateExpression cexpr : cexprs) {
168 value = cexpr.evaluate(this);
169 doPrint(cexpr.getInfo(), value);
170 }
171 }
172
173
174
175
176
177
178
179
180
181
182 private void doPrint(final JexlInfo info, final Object arg) {
183 try {
184 if (writer != null) {
185 if (arg instanceof CharSequence) {
186 writer.write(arg.toString());
187 } else if (arg != null) {
188 final Object[] value = {arg};
189 final JexlUberspect uber = jexl.getUberspect();
190 final JexlMethod method = uber.getMethod(writer, "print", value);
191 if (method != null) {
192 method.invoke(writer, value);
193 } else {
194 writer.write(arg.toString());
195 }
196 }
197 }
198 } catch (final java.io.IOException xio) {
199 throw TemplateEngine.createException(info, "call print", null, xio);
200 } catch (final java.lang.Exception xany) {
201 throw TemplateEngine.createException(info, "invoke print", null, xany);
202 }
203 }
204
205 @Override
206 protected Object resolveNamespace(final String prefix, final JexlNode node) {
207 return "jexl".equals(prefix)? this : super.resolveNamespace(prefix, node);
208 }
209
210 @Override
211 protected Object visit(final ASTIdentifier node, final Object data) {
212 final String name = node.getName();
213 if ("$jexl".equals(name)) {
214 return writer;
215 }
216 return super.visit(node, data);
217 }
218
219
220
221
222
223
224
225
226
227 @Override
228 protected Object visit(final ASTFunctionNode node, final Object data) {
229 final int argc = node.jjtGetNumChildren();
230 if (argc == 2) {
231 final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0);
232 if ("jexl".equals(functionNode.getNamespace())) {
233 final String functionName = functionNode.getName();
234 final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
235 if ("print".equals(functionName)) {
236
237 final Object[] argv = visit(argNode, null);
238 if (argv != null && argv.length > 0 && argv[0] instanceof Number) {
239 print(((Number) argv[0]).intValue());
240 return null;
241 }
242 }
243 if ("include".equals(functionName)) {
244
245 Object[] argv = visit(argNode, null);
246 if (argv != null && argv.length > 0) {
247 if (argv[0] instanceof TemplateScript) {
248 final TemplateScript script = (TemplateScript) argv[0];
249 if (argv.length > 1) {
250 argv = Arrays.copyOfRange(argv, 1, argv.length);
251 } else {
252 argv = null;
253 }
254 include(script, argv);
255 return null;
256 }
257 }
258 }
259
260 throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + functionName, null);
261 }
262 }
263 return super.visit(node, data);
264 }
265
266 @Override
267 protected Object visit(final ASTJexlScript script, final Object data) {
268 if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) {
269 return new Closure(this, (ASTJexlLambda) script) {
270 @Override
271 protected Interpreter createInterpreter(final JexlContext context, final Frame local, final JexlOptions options) {
272 final TemplateInterpreter.Arguments targs = new TemplateInterpreter.Arguments(jexl)
273 .context(context)
274 .options(options)
275 .frame(local)
276 .expressions(exprs)
277 .writer(writer);
278 return jexl.createTemplateInterpreter(targs);
279 }
280 };
281 }
282
283 final int numChildren = script.jjtGetNumChildren();
284 Object result = null;
285 for (int i = 0; i < numChildren; i++) {
286 final JexlNode child = script.jjtGetChild(i);
287 result = child.jjtAccept(this, data);
288 cancelCheck(child);
289 }
290 return result;
291 }
292
293 }