Author: henrib Date: Thu Aug 27 20:00:20 2015 New Revision: 1698223 URL: http://svn.apache.org/r1698223 Log: JEXL: Refactored template code into multiple files
Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java (with props) commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java (with props) Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java?rev=1698223&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java (added) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java Thu Aug 27 20:00:20 2015 @@ -0,0 +1,291 @@ +/* + * 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.JxltEngine; +import org.apache.commons.jexl3.internal.TemplateEngine.CompositeExpression; +import org.apache.commons.jexl3.internal.TemplateEngine.ConstantExpression; +import org.apache.commons.jexl3.internal.TemplateEngine.DeferredExpression; +import org.apache.commons.jexl3.internal.TemplateEngine.ImmediateExpression; +import org.apache.commons.jexl3.internal.TemplateEngine.NestedExpression; +import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression; + +import org.apache.commons.jexl3.parser.ASTBlock; +import org.apache.commons.jexl3.parser.ASTFunctionNode; +import org.apache.commons.jexl3.parser.ASTIdentifier; +import org.apache.commons.jexl3.parser.ASTJexlScript; +import org.apache.commons.jexl3.parser.ASTNumberLiteral; +import org.apache.commons.jexl3.parser.JexlNode; + +/** + * A visitor for templates. + * <p>A friend (ala C++) of template engine. + */ +public class TemplateDebugger extends Debugger { + /** The outer script. */ + private ASTJexlScript script; + /** The expressions called by the script through jexl:print. */ + private TemplateExpression[] exprs; + + /** + * Default ctor. + */ + public TemplateDebugger() { + } + + /** + * Position the debugger on the root of a template expression. + * @param je the expression + * @return true if the expression was a {@link TemplateExpression} instance, false otherwise + */ + public boolean debug(JxltEngine.Expression je) { + if (je instanceof TemplateExpression) { + TemplateEngine.TemplateExpression te = (TemplateEngine.TemplateExpression) je; + return visit(te, this) != null; + } else { + return false; + } + } + + /** + * Position the debugger on the root of a template script. + * @param jt the template + * @return true if the template was a {@link TemplateScript} instance, false otherwise + */ + public boolean debug(JxltEngine.Template jt) { + if (jt instanceof TemplateScript) { + TemplateScript ts = (TemplateScript) jt; + // ensure expr is not null for templates + this.exprs = ts.getExpressions() == null? new TemplateExpression[0] : ts.getExpressions(); + this.script = ts.getScript(); + start = 0; + end = 0; + indentLevel = 0; + builder.setLength(0); + cause = script; + int num = script.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + JexlNode child = script.jjtGetChild(i); + acceptStatement(child, null); + } + if (builder.length() > 0 && builder.charAt(builder.length() - 1) != '\n') { + builder.append('\n'); + } + end = builder.length(); + return end > 0; + } else { + return false; + } + } + + + @Override + protected Object visit(ASTBlock node, Object data) { + // if not really a template, use super impl + if (exprs == null) { + return super.visit(node, data); + } + // open the block + builder.append('{'); + if (indent > 0) { + indentLevel += 1; + builder.append('\n'); + } else { + builder.append(' '); + } + int num = node.jjtGetNumChildren(); + for (int i = 0; i < num; ++i) { + JexlNode child = node.jjtGetChild(i); + acceptStatement(child, data); + } + // before we close this block node, $$ might be needed + newJexlLine(); + if (indent > 0) { + indentLevel -= 1; + for (int i = 0; i < indentLevel; ++i) { + for(int s = 0; s < indent; ++s) { + builder.append(' '); + } + } + } + builder.append('}'); + // closed the block + return data; + } + + @Override + protected Object acceptStatement(JexlNode child, Object data) { + // if not really a template, use super impl + if (exprs != null) { + int printe = getPrintStatement(child); + if (printe >= 0) { + // statement is an expr + TemplateExpression te = exprs[printe]; + return visit(te, data); + } + // if statement is not a jexl:print(...), need to prepend '$$' + newJexlLine(); + } + return super.acceptStatement(child, data); + } + + /** + * In a template, any statement that is not 'jexl:print(n)' must be prefixed by "$$". + * @param child the node to check + * @return the expression number or -1 if the node is not a jexl:print + */ + private int getPrintStatement(JexlNode child) { + if (child instanceof ASTFunctionNode) { + ASTFunctionNode node = (ASTFunctionNode) child; + int num = node.jjtGetNumChildren(); + if (num == 3) { + ASTIdentifier ns = (ASTIdentifier) node.jjtGetChild(0); + ASTIdentifier fn = (ASTIdentifier) node.jjtGetChild(1); + JexlNode args = node.jjtGetChild(2); + if ("jexl".equals(ns.getName()) + && "print".equals(fn.getName()) + && args.jjtGetNumChildren() == 1 + && args.jjtGetChild(0) instanceof ASTNumberLiteral) { + ASTNumberLiteral exprn = (ASTNumberLiteral) args.jjtGetChild(0); + int n = exprn.getLiteral().intValue(); + if (exprs != null && n >= 0 && n < exprs.length) { + return n; + } + } + } + } + return -1; + } + + /** + * Insert $$ and \n when needed. + */ + private void newJexlLine() { + int length = builder.length(); + if (length == 0) { + builder.append("$$ "); + } else { + for (int i = length - 1; i >= 0; --i) { + char c = builder.charAt(i); + if (c == '\n') { + builder.append("$$ "); + break; + } + if (c == '}') { + builder.append("\n$$ "); + break; + } + if (c != ' ') { + break; + } + } + } + } + + /** + * Visit a template expression. + * @param expr the constant expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(TemplateExpression expr, Object data) { + Object r; + switch (expr.getType()) { + case CONSTANT: + r = visit((ConstantExpression) expr, data); + break; + case IMMEDIATE: + r = visit((ImmediateExpression) expr, data); + break; + case DEFERRED: + r = visit((DeferredExpression) expr, data); + break; + case NESTED: + r = visit((NestedExpression) expr, data); + break; + case COMPOSITE: + r = visit((CompositeExpression) expr, data); + break; + default: + r = null; + } + return r; + } + + /** + * Visit a constant expression. + * @param expr the constant expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(ConstantExpression expr, Object data) { + expr.asString(builder); + return data; + } + + /** + * Visit an immediate expression. + * @param expr the immediate expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(ImmediateExpression expr, Object data) { + builder.append(expr.isImmediate() ? '$' : '#'); + builder.append('{'); + super.accept(expr.node, data); + builder.append('}'); + return data; + } + + /** + * Visit a deferred expression. + * @param expr the deferred expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(DeferredExpression expr, Object data) { + builder.append(expr.isImmediate() ? '$' : '#'); + builder.append('{'); + super.accept(expr.node, data); + builder.append('}'); + return data; + } + + /** + * Visit a nested expression. + * @param expr the nested expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(NestedExpression expr, Object data) { + super.accept(expr.node, data); + return data; + } + /** + * Visit a composite expression. + * @param expr the composite expression + * @param data the visitor argument + * @return the visitor argument + */ + private Object visit(CompositeExpression expr, Object data) { + for (TemplateExpression ce : expr.exprs) { + visit(ce, data); + } + return data; + } + +} Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateDebugger.java ------------------------------------------------------------------------------ svn:eol-style = native 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=1698223&r1=1698222&r2=1698223&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 Thu Aug 27 20:00:20 2015 @@ -17,11 +17,9 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlContext; -import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlException; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JxltEngine; -import org.apache.commons.jexl3.internal.Engine.VarCollector; import org.apache.commons.jexl3.introspection.JexlMethod; import org.apache.commons.jexl3.introspection.JexlUberspect; import org.apache.commons.jexl3.parser.ASTJexlScript; @@ -45,10 +43,10 @@ import java.util.Set; * @since 3.0 */ public final class TemplateEngine extends JxltEngine { - /** The JEXL engine instance. */ - private final Engine jexl; /** The TemplateExpression cache. */ private final Engine.SoftCache<String, TemplateExpression> cache; + /** The JEXL engine instance. */ + private final Engine jexl; /** The first character for immediate expressions. */ private final char immediateChar; /** The first character for deferred expressions. */ @@ -73,11 +71,25 @@ public final class TemplateEngine extend } /** + * @return the immediate character + */ + char getImmediateChar() { + return immediateChar; + } + + /** + * @return the deferred character + */ + char getDeferredChar() { + return deferredChar; + } + + /** * Types of expressions. * Each instance carries a counter index per (composite sub-) template expression type. * @see ExpressionBuilder */ - private static enum ExpressionType { + static enum ExpressionType { /** Constant TemplateExpression, count index 0. */ CONSTANT(0), /** Immediate TemplateExpression, count index 1. */ @@ -105,7 +117,7 @@ public final class TemplateEngine extend * A helper class to build expressions. * Keeps count of sub-expressions by type. */ - private static final class ExpressionBuilder { + static final class ExpressionBuilder { /** Per TemplateExpression type counters. */ private final int[] counts; /** The list of expressions. */ @@ -181,7 +193,7 @@ public final class TemplateEngine extend * @return the JexlEngine */ @Override - public JexlEngine getEngine() { + public Engine getEngine() { return jexl; } @@ -198,7 +210,7 @@ public final class TemplateEngine extend /** * The abstract base class for all unified expressions, immediate '${...}' and deferred '#{...}'. */ - private abstract class TemplateExpression implements Expression { + abstract class TemplateExpression implements Expression { /** The source of this template expression(see {@link TemplateEngine.TemplateExpression#prepare}). */ protected final TemplateExpression source; @@ -347,7 +359,7 @@ public final class TemplateEngine extend } /** A constant unified expression. */ - private class ConstantExpression extends TemplateExpression { + class ConstantExpression extends TemplateExpression { /** The constant held by this unified expression. */ private final Object value; @@ -391,7 +403,7 @@ public final class TemplateEngine extend } /** The base for Jexl based unified expressions. */ - private abstract class JexlBasedExpression extends TemplateExpression { + abstract class JexlBasedExpression extends TemplateExpression { /** The JEXL string for this unified expression. */ protected final CharSequence expr; /** The JEXL node for this unified expression. */ @@ -442,7 +454,7 @@ public final class TemplateEngine extend } /** An immediate unified expression: ${jexl}. */ - private class ImmediateExpression extends JexlBasedExpression { + class ImmediateExpression extends JexlBasedExpression { /** * Creates an immediate unified expression. * @param expr the unified expression as a string @@ -467,7 +479,7 @@ public final class TemplateEngine extend } /** A deferred unified expression: #{jexl}. */ - private class DeferredExpression extends JexlBasedExpression { + class DeferredExpression extends JexlBasedExpression { /** * Creates a deferred unified expression. * @param expr the unified expression as a string @@ -504,7 +516,7 @@ public final class TemplateEngine extend * #{...${jexl}...} * Note that the deferred syntax is JEXL's. */ - private class NestedExpression extends JexlBasedExpression { + class NestedExpression extends JexlBasedExpression { /** * Creates a nested unified expression. * @param expr the unified expression as a string @@ -548,7 +560,7 @@ public final class TemplateEngine extend } /** A composite unified expression: "... ${...} ... #{...} ...". */ - private class CompositeExpression extends TemplateExpression { + class CompositeExpression extends TemplateExpression { /** Bit encoded (deferred count > 0) bit 1, (immediate count > 0) bit 0. */ private final int meta; /** The list of sub-expression resulting from parsing. */ @@ -732,7 +744,7 @@ public final class TemplateEngine extend * @return the unified expression instance * @throws JexlException if an error occur during parsing */ - private TemplateExpression parseExpression(JexlInfo info, String expr, Scope scope) { + TemplateExpression parseExpression(JexlInfo info, String expr, Scope scope) { final int size = expr.length(); final ExpressionBuilder builder = new ExpressionBuilder(0); final StringBuilder strb = new StringBuilder(size); @@ -899,7 +911,7 @@ public final class TemplateEngine extend /** * The enum capturing the difference between verbatim and code source fragments. */ - private static enum BlockType { + static enum BlockType { /** Block is to be output "as is" but may be a unified expression. */ VERBATIM, /** Block is a directive, ie a fragment of JEXL code. */ @@ -909,7 +921,7 @@ public final class TemplateEngine extend /** * Abstract the source fragments, verbatim or immediate typed text blocks. */ - private static final class Block { + static final class Block { /** The type of block, verbatim or directive. */ private final BlockType type; /** The block start line info. */ @@ -920,7 +932,7 @@ public final class TemplateEngine extend /** * Creates a new block. * @param theType the block type - * @param theLine the line number + * @param theLine the line number * @param theBlock the content */ Block(BlockType theType, int theLine, String theBlock) { @@ -929,20 +941,44 @@ public final class TemplateEngine extend body = theBlock; } + /** + * @return type + */ + BlockType getType() { + return type; + } + + /** + * @return line + */ + int getLine() { + return line; + } + + /** + * @return body + */ + String getBody() { + return body; + } + @Override public String toString() { if (BlockType.VERBATIM.equals(type)) { return body; } else { StringBuilder strb = new StringBuilder(64); - toString(strb, "$$"); + Iterator<CharSequence> lines = readLines(new StringReader(body)); + while (lines.hasNext()) { + strb.append("$$").append(lines.next()); + } return strb.toString(); } } /** * Appends this block string representation to a builder. - * @param strb the string builder to append to + * @param strb the string builder to append to * @param prefix the line prefix (immediate or deferred) */ protected void toString(StringBuilder strb, String prefix) { @@ -951,165 +987,12 @@ public final class TemplateEngine extend } else { Iterator<CharSequence> lines = readLines(new StringReader(body)); while (lines.hasNext()) { - strb.append(prefix); - strb.append(lines.next()); + strb.append(prefix).append(lines.next()); } } } } - /** - * A Template instance. - */ - public final class TemplateScript implements Template { - /** The prefix marker. */ - private final String prefix; - /** The array of source blocks. */ - private final Block[] source; - /** The resulting script. */ - private final ASTJexlScript script; - /** The TemplateEngine expressions called by the script. */ - private final TemplateExpression[] exprs; - - /** - * Creates a new template from an character input. - * @param info the source info - * @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{" - * since this would preclude being able to differentiate directives and template expressions - * @param reader the input reader - * @param parms the parameter names - * @throws NullPointerException if either the directive prefix or input is null - * @throws IllegalArgumentException if the directive prefix is invalid - */ - public TemplateScript(JexlInfo info, String directive, Reader reader, String... parms) { - if (directive == null) { - throw new NullPointerException("null prefix"); - } - if (Character.toString(immediateChar).equals(directive) - || (Character.toString(immediateChar) + "{").equals(directive) - || Character.toString(deferredChar).equals(directive) - || (Character.toString(deferredChar) + "{").equals(directive)) { - throw new IllegalArgumentException(directive + ": is not a valid directive pattern"); - } - if (reader == null) { - throw new NullPointerException("null input"); - } - Scope scope = parms == null ? null : new Scope(null, parms); - prefix = directive; - List<Block> blocks = readTemplate(prefix, reader); - List<TemplateExpression> uexprs = new ArrayList<TemplateExpression>(); - StringBuilder strb = new StringBuilder(); - int nuexpr = 0; - int codeStart = -1; - for (int b = 0; b < blocks.size(); ++b) { - Block block = blocks.get(b); - if (block.type == BlockType.VERBATIM) { - strb.append("jexl:print("); - strb.append(nuexpr++); - strb.append(");"); - } else { - // keep track of first block of code, the frame creator - if (codeStart < 0) { - codeStart = b; - } - strb.append(block.body); - } - } - // create the script - if (info == null) { - info = jexl.createInfo(); - } - // allow lambda defining params - script = jexl.parse(info.at(0, 0), strb.toString(), scope, false, false).script(); - scope = script.getScope(); - // createExpression the exprs using the code frame for those appearing after the first block of code - for (int b = 0; b < blocks.size(); ++b) { - Block block = blocks.get(b); - if (block.type == BlockType.VERBATIM) { - uexprs.add(parseExpression(info.at(block.line, 0), block.body, b > codeStart ? scope : null)); - } - } - source = blocks.toArray(new Block[blocks.size()]); - exprs = uexprs.toArray(new TemplateExpression[uexprs.size()]); - } - - /** - * Private ctor used to expand deferred expressions during prepare. - * @param thePrefix the directive prefix - * @param theSource the source - * @param theScript the script - * @param theExprs the expressions - */ - private TemplateScript(String thePrefix, Block[] theSource, - ASTJexlScript theScript, TemplateExpression[] theExprs) { - prefix = thePrefix; - source = theSource; - script = theScript; - exprs = theExprs; - } - - @Override - public String toString() { - StringBuilder strb = new StringBuilder(); - for (Block block : source) { - block.toString(strb, prefix); - } - return strb.toString(); - } - - @Override - public String asString() { - StringBuilder strb = new StringBuilder(); - int e = 0; - for (int b = 0; b < source.length; ++b) { - Block block = source[b]; - if (block.type == BlockType.DIRECTIVE) { - strb.append(prefix); - } else { - exprs[e++].asString(strb); - } - } - return strb.toString(); - } - - @Override - public TemplateScript prepare(JexlContext context) { - Scope.Frame frame = script.createFrame((Object[]) null); - TemplateContext tcontext = 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); - } - return new TemplateScript(prefix, source, script, immediates); - } - - @Override - public void evaluate(JexlContext context, Writer writer) { - evaluate(context, writer, (Object[]) null); - } - - @Override - public void evaluate(JexlContext context, Writer writer, Object... args) { - Scope.Frame frame = script.createFrame(args); - TemplateContext tcontext = new TemplateContext(context, frame, exprs, writer); - Interpreter interpreter = jexl.createInterpreter(tcontext, frame); - interpreter.interpret(script); - } - - @Override - public Set<List<String>> getVariables() { - VarCollector collector = new VarCollector(); - for (TemplateExpression expr : exprs) { - expr.getVariables(collector); - } - return collector.collected(); - } - - @Override - public String[] getParameters() { - return script.getParameters(); - } - } /** * The type of context to use during evaluation of templates. @@ -1292,7 +1175,7 @@ public final class TemplateEngine extend boolean eol = false; try { while ((c = reader.read()) >= 0) { - if (eol && (c != '\n' && c != '\r')) { + if (eol) {// && (c != '\n' && c != '\r')) { reader.reset(); break; } @@ -1405,7 +1288,6 @@ public final class TemplateEngine extend @Override public TemplateScript createTemplate(JexlInfo info, String prefix, Reader source, String... parms) { - return new TemplateScript(info, prefix, source, parms); + return new TemplateScript(this, info, prefix, source, parms); } - } \ No newline at end of file Added: 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=1698223&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java (added) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java Thu Aug 27 20:00:20 2015 @@ -0,0 +1,213 @@ +/* + * 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.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; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * A Template instance. + */ +public final class TemplateScript implements JxltEngine.Template { + /** The prefix marker. */ + private final String prefix; + /** The array of source blocks. */ + private final Block[] source; + /** The resulting script. */ + private final ASTJexlScript script; + /** The TemplateEngine expressions called by the script. */ + private final TemplateExpression[] exprs; + /** The engine. */ + private final TemplateEngine jxlt; + + /** + * Creates a new template from an character input. + * @param engine the template engine + * @param info the source info + * @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{" + since this would preclude being able to differentiate directives and jxlt expressions + * @param reader the input reader + * @param parms the parameter names + * @throws NullPointerException if either the directive prefix or input is null + * @throws IllegalArgumentException if the directive prefix is invalid + */ + public TemplateScript(TemplateEngine engine, JexlInfo info, String directive, Reader reader, String... parms) { + if (directive == null) { + throw new NullPointerException("null prefix"); + } + if (Character.toString(engine.getImmediateChar()).equals(directive) + || (Character.toString(engine.getImmediateChar()) + "{").equals(directive) + || Character.toString(engine.getDeferredChar()).equals(directive) + || (Character.toString(engine.getDeferredChar()) + "{").equals(directive)) { + throw new IllegalArgumentException(directive + ": is not a valid directive pattern"); + } + if (reader == null) { + throw new NullPointerException("null input"); + } + this.jxlt = engine; + Scope scope = parms == null ? null : new Scope(null, parms); + prefix = directive; + List<Block> blocks = jxlt.readTemplate(prefix, reader); + List<TemplateExpression> uexprs = new ArrayList<TemplateExpression>(); + StringBuilder strb = new StringBuilder(); + int nuexpr = 0; + int codeStart = -1; + for (int b = 0; b < blocks.size(); ++b) { + Block block = blocks.get(b); + if (block.getType() == BlockType.VERBATIM) { + strb.append("jexl:print("); + strb.append(nuexpr++); + strb.append(");\n"); + } else { + // keep track of first block of code, the frame creator + if (codeStart < 0) { + codeStart = b; + } + strb.append(block.getBody()); + } + } + // create the script + if (info == null) { + info = jxlt.getEngine().createInfo(); + } + // allow lambda defining params + script = jxlt.getEngine().parse(info.at(0, 0), strb.toString(), scope, false, false).script(); + scope = script.getScope(); + // create the exprs using the code frame for those appearing after the first block of code + for (int b = 0; b < blocks.size(); ++b) { + Block block = blocks.get(b); + if (block.getType() == BlockType.VERBATIM) { + uexprs.add( + jxlt.parseExpression( + info.at(block.getLine(), 0), + block.getBody(), + b > codeStart ? scope : null) + ); + } + } + source = blocks.toArray(new Block[blocks.size()]); + exprs = uexprs.toArray(new TemplateExpression[uexprs.size()]); + } + + /** + * Private ctor used to expand deferred expressions during prepare. + * @param engine the template engine + * @param thePrefix the directive prefix + * @param theSource the source + * @param theScript the script + * @param theExprs the expressions + */ + TemplateScript(TemplateEngine engine, + String thePrefix, + Block[] theSource, + ASTJexlScript theScript, + TemplateExpression[] theExprs) { + jxlt = engine; + prefix = thePrefix; + source = theSource; + script = theScript; + exprs = theExprs; + } + + /** + * @return script + */ + ASTJexlScript getScript() { + return script; + } + + /** + * @return exprs + */ + TemplateExpression[] getExpressions() { + return exprs; + } + + @Override + public String toString() { + StringBuilder strb = new StringBuilder(); + for (Block block : source) { + block.toString(strb, prefix); + } + return strb.toString(); + } + + @Override + public String asString() { + StringBuilder strb = new StringBuilder(); + int e = 0; + for (int b = 0; b < source.length; ++b) { + Block block = source[b]; + if (block.getType() == BlockType.DIRECTIVE) { + strb.append(prefix); + } else { + exprs[e++].asString(strb); + } + } + return strb.toString(); + } + + @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); + } + return new TemplateScript(jxlt, prefix, source, script, immediates); + } + + @Override + public void evaluate(JexlContext context, Writer writer) { + evaluate(context, writer, (Object[]) null); + } + + @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.interpret(script); + } + + @Override + public Set<List<String>> getVariables() { + Engine.VarCollector collector = new Engine.VarCollector(); + for (TemplateExpression expr : exprs) { + expr.getVariables(collector); + } + return collector.collected(); + } + + @Override + public String[] getParameters() { + return script.getParameters(); + } + +} Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java?rev=1698223&r1=1698222&r2=1698223&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java Thu Aug 27 20:00:20 2015 @@ -449,7 +449,7 @@ public class ArithmeticTest extends Jexl Object r1 = jexl.createExpression("463.0B + 0.1B").evaluate(jc); Assert.assertEquals(java.math.BigDecimal.class, r1.getClass()); } - + public void testMinusClass() throws Exception { JexlEngine jexl = new JexlBuilder().create(); JexlContext jc = new MapContext(); @@ -928,6 +928,28 @@ public class ArithmeticTest extends Jexl } } + public class Callable173 { + public Object call(String... arg) { + return 42; + } + public Object call(Integer... arg) { + return arg[0] * arg[1]; + } + } + + @Test + public void testJexl173() throws Exception { + JexlEngine jexl = new JexlBuilder().create(); + JexlContext jc = new MapContext(); + Callable173 c173 = new Callable173(); + JexlScript e = jexl.createScript( "c173(9, 6)", "c173" ); + Object result = e.execute(jc, c173); + Assert.assertEquals(54, result); + e = jexl.createScript( "c173('fourty', 'two')", "c173" ); + result = e.execute(jc, c173); + Assert.assertEquals(42, result); + + } public static class Arithmetic132 extends JexlArithmetic { public Arithmetic132() { 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=1698223&r1=1698222&r2=1698223&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 Thu Aug 27 20:00:20 2015 @@ -22,9 +22,7 @@ import java.math.MathContext; import java.util.HashMap; import java.util.Map; import org.apache.commons.jexl3.internal.introspection.Uberspect; -import org.apache.commons.jexl3.introspection.JexlMethod; import java.io.File; -import java.io.StringWriter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays;