Author: henrib Date: Mon Sep 14 10:41:18 2015 New Revision: 1702901 URL: http://svn.apache.org/r1702901 Log: JEXL: Use derived interpreter instead of wrapping context in Jxlt engine so operator/method overloads (user defined) can be called
Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java (with props) Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java?rev=1702901&r1=1702900&r2=1702901&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java Mon Sep 14 10:41:18 2015 @@ -342,7 +342,7 @@ public class Engine extends JexlEngine { * @return an Interpreter */ protected Interpreter createInterpreter(JexlContext context, Scope.Frame frame) { - return new Interpreter(this, context == null ? EMPTY_CONTEXT : context, frame); + return new Interpreter(this, context, frame); } @Override Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java?rev=1702901&r1=1702900&r2=1702901&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java Mon Sep 14 10:41:18 2015 @@ -20,8 +20,6 @@ import org.apache.commons.jexl3.JexlCont import org.apache.commons.jexl3.JexlException; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JxltEngine; -import org.apache.commons.jexl3.introspection.JexlMethod; -import org.apache.commons.jexl3.introspection.JexlUberspect; import org.apache.commons.jexl3.parser.ASTJexlScript; import org.apache.commons.jexl3.parser.JexlNode; import org.apache.commons.jexl3.parser.StringParser; @@ -30,7 +28,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; -import java.io.Writer; import java.util.ArrayList; import java.util.Collections; @@ -282,10 +279,7 @@ public final class TemplateEngine extend @Override public final TemplateExpression prepare(JexlContext context) { - Scope.Frame frame = context instanceof TemplateContext - ? ((TemplateContext) context).getFrame() - : null; - return prepare(frame, context); + return prepare(null, context); } /** @@ -297,7 +291,7 @@ public final class TemplateEngine extend */ protected final TemplateExpression prepare(Scope.Frame frame, JexlContext context) { try { - Interpreter interpreter = jexl.createInterpreter(context, frame); + Interpreter interpreter = new TemplateInterpreter(jexl, context, frame, null, null); return prepare(interpreter); } catch (JexlException xjexl) { JexlException xuel = createException(xjexl.getInfo(), "prepare", this, xjexl); @@ -321,10 +315,7 @@ public final class TemplateEngine extend @Override public final Object evaluate(JexlContext context) { - Scope.Frame frame = context instanceof TemplateContext - ? ((TemplateContext) context).getFrame() - : null; - return evaluate(frame, context); + return evaluate(null, context); } /** @@ -336,10 +327,10 @@ public final class TemplateEngine extend */ protected final Object evaluate(Scope.Frame frame, JexlContext context) { try { - Interpreter interpreter = jexl.createInterpreter(context, frame); + Interpreter interpreter = new TemplateInterpreter(jexl, context, frame, null, null); return evaluate(interpreter); } catch (JexlException xjexl) { - JexlException xuel = createException(xjexl.getInfo(), "prepare", this, xjexl); + JexlException xuel = createException(xjexl.getInfo(), "evaluate", this, xjexl); if (jexl.isSilent()) { jexl.logger.warn(xuel.getMessage(), xuel.getCause()); return null; @@ -701,7 +692,7 @@ public final class TemplateEngine extend * @param xany the exception * @return an exception containing an explicit error message */ - private Exception createException(JexlInfo info, String action, TemplateExpression expr, java.lang.Exception xany) { + static Exception createException(JexlInfo info, String action, TemplateExpression expr, java.lang.Exception xany) { StringBuilder strb = new StringBuilder("failed to "); strb.append(action); if (expr != null) { @@ -994,146 +985,6 @@ public final class TemplateEngine extend } - /** - * The type of context to use during evaluation of templates. - * <p>This context exposes its writer as '$jexl' to the scripts.</p> - * <p>public for introspection purpose.</p> - */ - public final class TemplateContext implements JexlContext, JexlContext.NamespaceResolver { - /** The wrapped context. */ - private final JexlContext wrap; - /** The array of TemplateEngine expressions. */ - private final TemplateExpression[] exprs; - /** The writer used to output. */ - private final Writer writer; - /** The call frame. */ - private final Scope.Frame frame; - - /** - * Creates a TemplateScript context instance. - * @param jcontext the base context - * @param jframe the calling frame - * @param expressions the list of TemplateExpression from the TemplateScript to evaluate - * @param out the output writer - */ - protected TemplateContext(JexlContext jcontext, Scope.Frame jframe, - TemplateExpression[] expressions, Writer out) { - wrap = jcontext; - frame = jframe; - exprs = expressions; - writer = out; - } - - /** - * Gets this context calling frame. - * @return the engine frame - */ - public Scope.Frame getFrame() { - return frame; - } - - @Override - public Object get(String name) { - if ("$jexl".equals(name)) { - return writer; - } else { - return wrap.get(name); - } - } - - @Override - public void set(String name, Object value) { - wrap.set(name, value); - } - - @Override - public boolean has(String name) { - return wrap.has(name); - } - - @Override - public Object resolveNamespace(String ns) { - if ("jexl".equals(ns)) { - return this; - } else if (wrap instanceof JexlContext.NamespaceResolver) { - return ((JexlContext.NamespaceResolver) wrap).resolveNamespace(ns); - } else { - return null; - } - } - - /** - * Includes a call to another template. - * <p>Includes another template using this template initial context and writer.</p> - * @param script the TemplateScript to evaluate - * @param args the arguments - */ - public void include(TemplateScript script, Object... args) { - script.evaluate(wrap, writer, args); - } - - /** - * Prints a unified expression evaluation result. - * @param e the expression number - */ - public void print(int e) { - if (e < 0 || e >= exprs.length) { - return; - } - TemplateExpression expr = exprs[e]; - if (expr.isDeferred()) { - expr = expr.prepare(frame, wrap); - } - if (expr instanceof CompositeExpression) { - printComposite((CompositeExpression) expr); - } else { - doPrint(expr.getInfo(), expr.evaluate(frame, this)); - } - } - - /** - * Prints a composite expression. - * @param composite the composite expression - */ - protected void printComposite(CompositeExpression composite) { - TemplateExpression[] cexprs = composite.exprs; - final int size = cexprs.length; - Object value; - for (int e = 0; e < size; ++e) { - value = cexprs[e].evaluate(frame, this); - doPrint(cexprs[e].getInfo(), value); - } - } - - /** - * Prints to output. - * <p>This will dynamically try to find the best suitable method in the writer through uberspection. - * Subclassing Writer by adding 'print' methods should be the preferred way to specialize output. - * </p> - * @param info the source info - * @param arg the argument to print out - */ - private void doPrint(JexlInfo info, Object arg) { - try { - if (arg instanceof CharSequence) { - writer.write(arg.toString()); - } else if (arg != null) { - Object[] value = {arg}; - JexlUberspect uber = getEngine().getUberspect(); - JexlMethod method = uber.getMethod(writer, "print", value); - if (method != null) { - method.invoke(writer, value); - } else { - writer.write(arg.toString()); - } - } - } catch (java.io.IOException xio) { - throw createException(info, "call print", null, xio); - } catch (java.lang.Exception xany) { - throw createException(info, "invoke print", null, xany); - } - } - } /** * Whether a sequence starts with a given set of characters (following spaces). Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java?rev=1702901&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java (added) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java Mon Sep 14 10:41:18 2015 @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.jexl3.internal; + +import org.apache.commons.jexl3.JexlContext; +import org.apache.commons.jexl3.JexlInfo; +import org.apache.commons.jexl3.JxltEngine; +import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression; +import org.apache.commons.jexl3.introspection.JexlMethod; +import org.apache.commons.jexl3.introspection.JexlUberspect; +import org.apache.commons.jexl3.parser.ASTArguments; +import org.apache.commons.jexl3.parser.ASTFunctionNode; +import org.apache.commons.jexl3.parser.ASTIdentifier; +import org.apache.commons.jexl3.parser.JexlNode; +import java.io.Writer; +import java.util.Arrays; + +/** + * The type of interpreter to use during evaluation of templates. + * <p>This context exposes its writer as '$jexl' to the scripts.</p> + * <p>public for introspection purpose.</p> + */ +public class TemplateInterpreter extends Interpreter { + /** The array of template expressions. */ + private final TemplateExpression[] exprs; + /** The writer used to output. */ + private final Writer writer; + + /** + * Creates a template interpreter instance. + * @param jexl the engine instance + * @param jcontext the base context + * @param jframe the calling frame + * @param expressions the list of TemplateExpression from the TemplateScript to evaluate + * @param out the output writer + */ + TemplateInterpreter(Engine jexl, + JexlContext jcontext, Scope.Frame jframe, TemplateExpression[] expressions, Writer out) { + super(jexl, jcontext, jframe); + exprs = expressions; + writer = out; + } + + /** + * Includes a call to another template. + * <p> + * Includes another template using this template initial context and writer.</p> + * @param script the TemplateScript to evaluate + * @param args the arguments + */ + public void include(TemplateScript script, Object... args) { + script.evaluate(context, writer, args); + } + + /** + * Prints a unified expression evaluation result. + * @param e the expression number + */ + public void print(int e) { + if (e < 0 || e >= exprs.length) { + return; + } + TemplateEngine.TemplateExpression expr = exprs[e]; + if (expr.isDeferred()) { + expr = expr.prepare(frame, context); + } + if (expr instanceof TemplateEngine.CompositeExpression) { + printComposite((TemplateEngine.CompositeExpression) expr); + } else { + doPrint(expr.getInfo(), expr.evaluate(this)); + } + } + + /** + * Prints a composite expression. + * @param composite the composite expression + */ + private void printComposite(TemplateEngine.CompositeExpression composite) { + TemplateEngine.TemplateExpression[] cexprs = composite.exprs; + final int size = cexprs.length; + Object value; + for (int e = 0; e < size; ++e) { + value = cexprs[e].evaluate(this); + doPrint(cexprs[e].getInfo(), value); + } + } + + /** + * Prints to output. + * <p> + * This will dynamically try to find the best suitable method in the writer through uberspection. + * Subclassing Writer by adding 'print' methods should be the preferred way to specialize output. + * </p> + * @param info the source info + * @param arg the argument to print out + */ + private void doPrint(JexlInfo info, Object arg) { + try { + if (writer != null) { + if (arg instanceof CharSequence) { + writer.write(arg.toString()); + } else if (arg != null) { + Object[] value = {arg}; + JexlUberspect uber = jexl.getUberspect(); + JexlMethod method = uber.getMethod(writer, "print", value); + if (method != null) { + method.invoke(writer, value); + } else { + writer.write(arg.toString()); + } + } + } + } catch (java.io.IOException xio) { + throw TemplateEngine.createException(info, "call print", null, xio); + } catch (java.lang.Exception xany) { + throw TemplateEngine.createException(info, "invoke print", null, xany); + } + } + + @Override + protected Object resolveNamespace(String prefix, JexlNode node) { + return "jexl".equals(prefix)? this : super.resolveNamespace(prefix, node); + } + + @Override + protected Object visit(ASTFunctionNode node, Object data) { + int argc = node.jjtGetNumChildren(); + if (argc > 2) { + // objectNode 0 is the prefix + String prefix = ((ASTIdentifier) node.jjtGetChild(0)).getName(); + if ("jexl".equals(prefix)) { + ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(1); + ASTArguments argNode = (ASTArguments) node.jjtGetChild(2); + String fname = functionNode.getName(); + if ("print".equals(fname)) { + // evaluate the arguments + Object[] argv = visit(argNode, null); + print((Integer) argv[0]); + return null; + } + if ("include".equals(fname)) { + // evaluate the arguments + Object[] argv = visit(argNode, null); + if (argv != null && argv.length > 0) { + if (argv[0] instanceof TemplateScript) { + TemplateScript script = (TemplateScript) argv[0]; + if (argv.length > 1) { + argv = Arrays.copyOfRange(argv, 1, argv.length); + } else { + argv = null; + } + include(script, argv); + return null; + } + } + } + // fail safe + throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + fname, null); + } + } + return super.visit(node, data); + } + + @Override + protected Object visit(ASTIdentifier node, Object data) { + String name = node.getName(); + if ("$jexl".equals(name)) { + return writer; + } + return super.visit(node, data); + } + +} Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java?rev=1702901&r1=1702900&r2=1702901&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java Mon Sep 14 10:41:18 2015 @@ -21,7 +21,6 @@ import org.apache.commons.jexl3.JexlInfo import org.apache.commons.jexl3.JxltEngine; import org.apache.commons.jexl3.internal.TemplateEngine.Block; import org.apache.commons.jexl3.internal.TemplateEngine.BlockType; -import org.apache.commons.jexl3.internal.TemplateEngine.TemplateContext; import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression; import org.apache.commons.jexl3.parser.ASTJexlScript; import java.io.Reader; @@ -175,10 +174,9 @@ public final class TemplateScript implem @Override public TemplateScript prepare(JexlContext context) { Scope.Frame frame = script.createFrame((Object[]) null); - TemplateContext tcontext = jxlt.new TemplateContext(context, frame, exprs, null); TemplateExpression[] immediates = new TemplateExpression[exprs.length]; for (int e = 0; e < exprs.length; ++e) { - immediates[e] = exprs[e].prepare(frame, tcontext); + immediates[e] = exprs[e].prepare(frame, context); } return new TemplateScript(jxlt, prefix, source, script, immediates); } @@ -191,8 +189,7 @@ public final class TemplateScript implem @Override public void evaluate(JexlContext context, Writer writer, Object... args) { Scope.Frame frame = script.createFrame(args); - TemplateContext tcontext = jxlt.new TemplateContext(context, frame, exprs, writer); - Interpreter interpreter = jxlt.getEngine().createInterpreter(tcontext, frame); + Interpreter interpreter = new TemplateInterpreter(jxlt.getEngine(), context, frame, exprs, writer); interpreter.interpret(script); } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java?rev=1702901&r1=1702900&r2=1702901&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java Mon Sep 14 10:41:18 2015 @@ -26,6 +26,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.jexl3.junit.Asserter; +import java.io.StringWriter; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -315,7 +316,6 @@ public class ArithmeticOperatorTest exte } } - protected Object setDateValue(Date date, String key, Object value) throws Exception { Calendar cal = Calendar.getInstance(); cal.setTime(date); @@ -345,10 +345,6 @@ public class ArithmeticOperatorTest exte public Object arraySet(Date date, String identifier, Object value) throws Exception { return setDateValue(date, identifier, value); } - - public String format(Number number, String fmt) { - return new DecimalFormat(fmt).format(number); - } } public static class DateContext extends MapContext { @@ -362,6 +358,10 @@ public class ArithmeticOperatorTest exte SimpleDateFormat sdf = new SimpleDateFormat(fmt, locale); return sdf.format(date); } + + public String format(Number number, String fmt) { + return new DecimalFormat(fmt).format(number); + } } @Test @@ -412,6 +412,37 @@ public class ArithmeticOperatorTest exte Assert.assertEquals("Wed 20 Aug 1969", s0); jc.setLocale(Locale.FRANCE); s0 = expr1.execute(jc, x0, "EEE dd MMM yyyy"); - Assert.assertEquals("mer. 20 août 1969", s0); + Assert.assertEquals("mer. 20 ao\u00fbt 1969", s0); + } + + @Test + public void testFormatArithmeticJxlt() throws Exception { + Map<String, Object> ns = new HashMap<String, Object>(); + ns.put("calc", Aggregate.class); + Calendar cal = Calendar.getInstance(); + cal.set(1969, 7, 20); + Date x0 = cal.getTime(); + String y0 = "yyy-MM-dd"; + DateContext jc = new DateContext(); + JexlEngine jexl = new JexlBuilder().cache(32).namespaces(ns).arithmetic(new DateArithmetic(true)).create(); + JxltEngine jxlt = jexl.createJxltEngine(); + + JxltEngine.Template expr0 = jxlt.createTemplate("${x.format(y)}", "x", "y"); + StringWriter strw = new StringWriter(); + expr0.evaluate(jc, strw, x0, y0); + String strws = strw.toString(); + Assert.assertEquals("1969-08-20", strws); + + expr0 = jxlt.createTemplate("${calc:sum(x .. y)}", "x", "y"); + strw = new StringWriter(); + expr0.evaluate(jc, strw, 1, 3); + strws = strw.toString(); + Assert.assertEquals("6", strws); + + JxltEngine.Template expr1 = jxlt.createTemplate("${jexl:include(s, x, y)}", "s", "x", "y"); + strw = new StringWriter(); + expr1.evaluate(jc, strw, expr0, 1, 3); + strws = strw.toString(); + Assert.assertEquals("6", strws); } } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java?rev=1702901&r1=1702900&r2=1702901&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Mon Sep 14 10:41:18 2015 @@ -24,12 +24,8 @@ import java.util.Map; import org.apache.commons.jexl3.internal.introspection.Uberspect; import java.io.File; import java.net.URL; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; import java.util.List; import java.util.Set; import org.junit.Assert;