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