You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2012/05/12 08:52:23 UTC

svn commit: r1337453 [2/3] - in /commons/proper/jexl/trunk: ./ src/main/java/org/apache/commons/jexl3/ src/main/java/org/apache/commons/jexl3/internal/ src/main/java/org/apache/commons/jexl3/internal/introspection/ src/main/java/org/apache/commons/jexl...

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=1337453&r1=1337452&r2=1337453&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 Sat May 12 06:52:21 2012
@@ -21,7 +21,6 @@ import org.apache.commons.jexl3.JexlBuil
 import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlException;
-import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.internal.introspection.SandboxUberspect;
@@ -37,21 +36,14 @@ import org.apache.commons.jexl3.parser.A
 import org.apache.commons.jexl3.parser.ASTMethodNode;
 import org.apache.commons.jexl3.parser.ASTReference;
 import org.apache.commons.jexl3.parser.JexlNode;
-import org.apache.commons.jexl3.parser.ParseException;
 import org.apache.commons.jexl3.parser.Parser;
-import org.apache.commons.jexl3.parser.TokenMgrError;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.Reader;
-import java.io.StringReader;
 
+import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
@@ -61,8 +53,6 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import java.lang.ref.SoftReference;
-import java.net.URL;
-import java.net.URLConnection;
 
 /**
  * A JexlEngine implementation.
@@ -99,7 +89,7 @@ public class Engine extends JexlEngine {
     };
 
     /**
-     *  Gets the default instance of Uberspect.
+     * Gets the default instance of Uberspect.
      * <p>This is lazily initialized to avoid building a default instance if there
      * is no use for it. The main reason for not using the default Uberspect instance is to
      * be able to use a (low level) introspector created with a given logger
@@ -153,6 +143,10 @@ public class Engine extends JexlEngine {
      */
     protected final SoftCache<String, ASTJexlScript> cache;
     /**
+     * The expression max length to hit the cache.
+     */
+    protected final int cacheThreshold;
+    /**
      * The default cache load factor.
      */
     private static final float LOAD_FACTOR = 0.75f;
@@ -187,10 +181,11 @@ public class Engine extends JexlEngine {
         this.strict = conf.strict() == null ? true : conf.strict().booleanValue();
         this.arithmetic = conf.arithmetic() == null ? new JexlArithmetic(this.strict) : conf.arithmetic();
         this.cache = conf.cache() <= 0 ? null : new SoftCache<String, ASTJexlScript>(conf.cache());
+        this.cacheThreshold = conf.cacheThreshold();
     }
 
     /**
-     *  Gets the default instance of Uberspect.
+     * Gets the default instance of Uberspect.
      * <p>This is lazily initialized to avoid building a default instance if there
      * is no use for it. The main reason for not using the default Uberspect instance is to
      * be able to use a (low level) introspector created with a given logger
@@ -241,90 +236,146 @@ public class Engine extends JexlEngine {
     }
 
     /**
-     * An overridable through covariant return JexlExpression creator.
-     * @param text the script text
-     * @param tree the parse AST tree
-     * @return the script instance
+     * A soft referenced cache.
+     * <p>The actual cache is held through a soft reference, allowing it to be GCed under
+     * memory pressure.</p>
+     * @param <K> the cache key entry type
+     * @param <V> the cache key value type
      */
-    protected JexlExpression createExpression(ASTJexlScript tree, String text) {
-        return new Script(this, text, tree);
-    }
+    protected class SoftCache<K, V> {
+        /**
+         * The cache size.
+         */
+        private final int size;
+        /**
+         * The soft reference to the cache map.
+         */
+        private SoftReference<Map<K, V>> ref = null;
 
-    @Override
-    public JexlExpression createExpression(String expression) {
-        return createExpression(expression, null);
-    }
+        /**
+         * Creates a new instance of a soft cache.
+         * @param theSize the cache size
+         */
+        SoftCache(int theSize) {
+            size = theSize;
+        }
 
-    @Override
-    public JexlExpression createExpression(String expression, JexlInfo info) {
-        // Parse the expression
-        ASTJexlScript tree = parse(expression, info, null);
-        if (tree.jjtGetNumChildren() > 1) {
-            logger.warn("The JEXL Expression created will be a reference"
-                    + " to the first expression from the supplied script: \"" + expression + "\" ");
+        /**
+         * Returns the cache size.
+         * @return the cache size
+         */
+        int size() {
+            return size;
         }
-        return createExpression(tree, expression);
-    }
 
-    @Override
-    public Script createScript(String scriptText) {
-        return createScript(scriptText, null, null);
+        /**
+         * Clears the cache.
+         */
+        void clear() {
+            ref = null;
+        }
+
+        /**
+         * Produces the cache entry set.
+         * @return the cache entry set
+         */
+        Set<Entry<K, V>> entrySet() {
+            Map<K, V> map = ref != null ? ref.get() : null;
+            return map != null ? map.entrySet() : Collections.<Entry<K, V>>emptySet();
+        }
+
+        /**
+         * Gets a value from cache.
+         * @param key the cache entry key
+         * @return the cache entry value
+         */
+        V get(K key) {
+            final Map<K, V> map = ref != null ? ref.get() : null;
+            return map != null ? map.get(key) : null;
+        }
+
+        /**
+         * Puts a value in cache.
+         * @param key    the cache entry key
+         * @param script the cache entry value
+         */
+        void put(K key, V script) {
+            Map<K, V> map = ref != null ? ref.get() : null;
+            if (map == null) {
+                map = createCache(size);
+                ref = new SoftReference<Map<K, V>>(map);
+            }
+            map.put(key, script);
+        }
     }
 
-    @Override
-    public Script createScript(String scriptText, String... names) {
-        return createScript(scriptText, null, names);
+    /**
+     * Creates a cache.
+     * @param <K>       the key type
+     * @param <V>       the value type
+     * @param cacheSize the cache size, must be > 0
+     * @return a Map usable as a cache bounded to the given size
+     */
+    protected <K, V> Map<K, V> createCache(final int cacheSize) {
+        return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true) {
+            /** Serial version UID. */
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+                return size() > cacheSize;
+            }
+        };
     }
 
     @Override
-    public Script createScript(String scriptText, JexlInfo info, String[] names) {
-        if (scriptText == null) {
-            throw new NullPointerException("scriptText is null");
+    public void clearCache() {
+        synchronized (parser) {
+            if (cache != null) {
+                cache.clear();
+            }
         }
-        // Parse the expression
-        ASTJexlScript tree = parse(scriptText, info, new Scope(null, names));
-        return createScript(tree, scriptText);
     }
 
     /**
-     * An overridable through covariant return Script creator.
-     * @param text the script text
-     * @param tree the parse AST tree
-     * @return the script instance
+     * Creates an interpreter.
+     * @param context a JexlContext; if null, the empty context is used instead.
+     * @param frame   the interpreter frame
+     * @return an Interpreter
      */
-    protected Script createScript(ASTJexlScript tree, String text) {
-        return new Script(this, text, tree);
+    protected Interpreter createInterpreter(JexlContext context, Scope.Frame frame) {
+        return new Interpreter(this, context == null ? EMPTY_CONTEXT : context, frame);
     }
 
     @Override
-    public Script createScript(File scriptFile) throws IOException {
-        if (scriptFile == null) {
-            throw new NullPointerException("scriptFile is null");
-        }
-        if (!scriptFile.canRead()) {
-            throw new IOException("Can't read scriptFile (" + scriptFile.getCanonicalPath() + ")");
+    public Script createScript(JexlInfo info, String scriptText, String[] names) {
+        if (scriptText == null) {
+            throw new NullPointerException("source is null");
         }
-        BufferedReader reader = new BufferedReader(new FileReader(scriptFile));
-        JexlInfo info = null;
-        if (debug) {
-            info = createInfo(scriptFile.getName(), 0, 0);
+        if (info == null && debug) {
+            info = createInfo();
         }
-        return createScript(readerToString(reader), info, null);
+        String source = trimSource(scriptText);
+        Scope scope = names == null ? null : new Scope(null, names);
+        ASTJexlScript tree = parse(info, source, scope, false);
+        return new Script(this, source, tree);
     }
 
     @Override
-    public JexlScript createScript(URL scriptUrl) throws IOException {
-        if (scriptUrl == null) {
-            throw new NullPointerException("scriptUrl is null");
+    public Script createExpression(JexlInfo info, String expression) {
+        if (expression == null) {
+            throw new NullPointerException("source is null");
         }
-        URLConnection connection = scriptUrl.openConnection();
-        BufferedReader reader = new BufferedReader(
-                new InputStreamReader(connection.getInputStream()));
-        JexlInfo info = null;
-        if (debug) {
-            info = createInfo(scriptUrl.toString(), 0, 0);
+        if (info == null && debug) {
+            info = createInfo();
         }
-        return createScript(readerToString(reader), info, null);
+        String source = trimSource(expression);
+        ASTJexlScript tree = parse(info, source, null, false);
+        if (tree.toExpression()) {
+            logger.warn("The JEXL Expression created will be a reference"
+                    + " to the first expression from the supplied script: \"" + expression + "\" ");
+        }
+        return new Script(this, source, tree);
     }
 
     @Override
@@ -338,14 +389,15 @@ public class Engine extends JexlEngine {
             context = EMPTY_CONTEXT;
         }
         // synthetize expr using register
-        expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
+        String src = trimSource(expr);
+        src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + ";";
         try {
-            parser.ALLOW_REGISTERS = true;
-            Scope scope = new Scope(null, "#0");
-            ASTJexlScript script = parse(expr, null, scope);
-            JexlNode node = script.jjtGetChild(0);
-            Scope.Frame frame = script.createFrame(bean);
-            Interpreter interpreter = createInterpreter(context, frame);
+            final JexlInfo info = debug ? createInfo() : null;
+            final Scope scope = new Scope(null, "#0");
+            final ASTJexlScript script = parse(info, src, scope, true);
+            final JexlNode node = script.jjtGetChild(0);
+            final Scope.Frame frame = script.createFrame(bean);
+            final Interpreter interpreter = createInterpreter(context, frame);
             return node.jjtAccept(interpreter, null);
         } catch (JexlException xjexl) {
             if (silent) {
@@ -353,8 +405,6 @@ public class Engine extends JexlEngine {
                 return null;
             }
             throw xjexl.clean();
-        } finally {
-            parser.ALLOW_REGISTERS = false;
         }
     }
 
@@ -369,14 +419,15 @@ public class Engine extends JexlEngine {
             context = EMPTY_CONTEXT;
         }
         // synthetize expr using registers
-        expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + "#1" + ";";
+        String src = trimSource(expr);
+        src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=" + "#1" + ";";
         try {
-            parser.ALLOW_REGISTERS = true;
-            Scope scope = new Scope(null, "#0", "#1");
-            ASTJexlScript script = parse(expr, null, scope);
-            JexlNode node = script.jjtGetChild(0);
-            Scope.Frame frame = script.createFrame(bean, value);
-            Interpreter interpreter = createInterpreter(context, frame);
+            final JexlInfo info = debug ? createInfo() : null;
+            final Scope scope = new Scope(null, "#0", "#1");
+            final ASTJexlScript script = parse(info, src, scope, true);
+            final JexlNode node = script.jjtGetChild(0);
+            final Scope.Frame frame = script.createFrame(bean, value);
+            final Interpreter interpreter = createInterpreter(context, frame);
             node.jjtAccept(interpreter, null);
         } catch (JexlException xjexl) {
             if (silent) {
@@ -384,8 +435,6 @@ public class Engine extends JexlEngine {
                 return;
             }
             throw xjexl.clean();
-        } finally {
-            parser.ALLOW_REGISTERS = false;
         }
     }
 
@@ -393,7 +442,7 @@ public class Engine extends JexlEngine {
     public Object invokeMethod(Object obj, String meth, Object... args) {
         JexlException xjexl = null;
         Object result = null;
-        final JexlInfo info = jexlInfo();
+        final JexlInfo info = debug ? createInfo() : null;
         try {
             JexlMethod method = uberspect.getMethod(obj, meth, args);
             if (method == null && arithmetic.narrowArguments(args)) {
@@ -404,6 +453,8 @@ public class Engine extends JexlEngine {
             } else {
                 xjexl = new JexlException.Method(info, meth, null);
             }
+        } catch (JexlException xany) {
+            xjexl = xany;
         } catch (Exception xany) {
             xjexl = new JexlException.Method(info, meth, xany);
         } finally {
@@ -432,13 +483,13 @@ public class Engine extends JexlEngine {
      * Creates a new instance of an object using the most appropriate constructor
      * based on the arguments.
      * @param clazz the class to instantiate
-     * @param args the constructor arguments
+     * @param args  the constructor arguments
      * @return the created object instance or null on failure when silent
      */
     protected Object doCreateInstance(Object clazz, Object... args) {
         JexlException xjexl = null;
         Object result = null;
-        final JexlInfo info = jexlInfo();
+        final JexlInfo info = debug ? createInfo() : null;
         try {
             JexlMethod ctor = uberspect.getConstructor(clazz, args);
             if (ctor == null && arithmetic.narrowArguments(args)) {
@@ -449,6 +500,8 @@ public class Engine extends JexlEngine {
             } else {
                 xjexl = new JexlException.Method(info, clazz.toString(), null);
             }
+        } catch (JexlException xany) {
+            xjexl = xany;
         } catch (Exception xany) {
             xjexl = new JexlException.Method(info, clazz.toString(), xany);
         } finally {
@@ -464,122 +517,12 @@ public class Engine extends JexlEngine {
     }
 
     /**
-     * Creates an interpreter.
-     * @param context a JexlContext; if null, the empty context is used instead.
-     * @param frame the interpreter frame
-     * @return an Interpreter
-     */
-    protected Interpreter createInterpreter(JexlContext context, Scope.Frame frame) {
-        return new Interpreter(this, context == null ? EMPTY_CONTEXT : context, frame);
-    }
-
-    /**
-     * A soft reference on cache.
-     * <p>The cache is held through a soft reference, allowing it to be GCed under
-     * memory pressure.</p>
-     * @param <K> the cache key entry type
-     * @param <V> the cache key value type
-     */
-    protected class SoftCache<K, V> {
-        /**
-         * The cache size.
-         */
-        private final int size;
-        /**
-         * The soft reference to the cache map.
-         */
-        private SoftReference<Map<K, V>> ref = null;
-
-        /**
-         * Creates a new instance of a soft cache.
-         * @param theSize the cache size
-         */
-        SoftCache(int theSize) {
-            size = theSize;
-        }
-
-        /**
-         * Returns the cache size.
-         * @return the cache size
-         */
-        int size() {
-            return size;
-        }
-
-        /**
-         * Clears the cache.
-         */
-        void clear() {
-            ref = null;
-        }
-
-        /**
-         * Produces the cache entry set.
-         * @return the cache entry set
-         */
-        Set<Entry<K, V>> entrySet() {
-            Map<K, V> map = ref != null ? ref.get() : null;
-            return map != null ? map.entrySet() : Collections.<Entry<K, V>>emptySet();
-        }
-
-        /**
-         * Gets a value from cache.
-         * @param key the cache entry key
-         * @return the cache entry value
-         */
-        V get(K key) {
-            final Map<K, V> map = ref != null ? ref.get() : null;
-            return map != null ? map.get(key) : null;
-        }
-
-        /**
-         * Puts a value in cache.
-         * @param key the cache entry key
-         * @param script the cache entry value
-         */
-        void put(K key, V script) {
-            Map<K, V> map = ref != null ? ref.get() : null;
-            if (map == null) {
-                map = createCache(size);
-                ref = new SoftReference<Map<K, V>>(map);
-            }
-            map.put(key, script);
-        }
-    }
-
-    /**
-     * Creates a cache.
-     * @param <K> the key type
-     * @param <V> the value type
-     * @param cacheSize the cache size, must be > 0
-     * @return a Map usable as a cache bounded to the given size
-     */
-    protected <K, V> Map<K, V> createCache(final int cacheSize) {
-        return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true) {
-            /** Serial version UID. */
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
-                return size() > cacheSize;
-            }
-        };
-    }
-
-    @Override
-    public void clearCache() {
-        synchronized (parser) {
-            cache.clear();
-        }
-    }
-
-    /**
      * Gets the list of variables accessed by a script.
      * <p>This method will visit all nodes of a script and extract all variables whether they
      * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
      * @param script the script
      * @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string)
-     *         or the empty set if no variables are used
+     * or the empty set if no variables are used
      */
     protected Set<List<String>> getVariables(JexlNode script) {
         Set<List<String>> refs = new LinkedHashSet<List<String>>();
@@ -591,7 +534,7 @@ public class Engine extends JexlEngine {
      * Fills up the list of variables accessed by a node.
      * @param node the node
      * @param refs the set of variable being filled
-     * @param ref the current variable being filled
+     * @param ref  the current variable being filled
      */
     protected void getVariables(JexlNode node, Set<List<String>> refs, List<String> ref) {
         if (node instanceof ASTIdentifier) {
@@ -681,92 +624,58 @@ public class Engine extends JexlEngine {
 
     /**
      * Parses an expression.
-     * @param expression the expression to parse
-     * @param info debug information structure
-     * @param frame the script frame to use
+     *
+     * @param info       information structure
+     * @param expr       the expression to parse
+     * @param scope      the script frame
+     * @param registers  whether the parser should allow the unnamed '#number' syntax for 'registers'
      * @return the parsed tree
      * @throws JexlException if any error occured during parsing
      */
-    protected ASTJexlScript parse(CharSequence expression, JexlInfo info, Scope frame) {
-        String expr = cleanExpression(expression);
-        ASTJexlScript script = null;
-        JexlInfo jexlInfo = null;
+    protected ASTJexlScript parse(JexlInfo info, String expr, Scope scope, boolean registers) {
+        final boolean cached = expr.length() < cacheThreshold && cache != null ;
+        ASTJexlScript script;
         synchronized (parser) {
-            if (cache != null) {
+            if (cached) {
                 script = cache.get(expr);
                 if (script != null) {
                     Scope f = script.getScope();
-                    if ((f == null && frame == null) || (f != null && f.equals(frame))) {
+                    if ((f == null && scope == null) || (f != null && f.equals(scope))) {
                         return script;
                     }
                 }
             }
-            try {
-                Reader reader = new StringReader(expr);
-                // use first calling method of JexlEngine as debug info
-                if (info == null) {
-                    jexlInfo = jexlInfo();
-                } else {
-                    jexlInfo = info;
-                }
-                script = parser.parse(reader, frame, jexlInfo);
-                if (cache != null) {
-                    cache.put(expr, script);
-                }
-            } catch (TokenMgrError xtme) {
-                throw new JexlException.Tokenization(jexlInfo, expression, xtme).clean();
-            } catch (ParseException xparse) {
-                throw new JexlException.Parsing(jexlInfo, expression, xparse).clean();
-            } finally {
-                parser.setFrame(null);
+            Reader reader = new StringReader(expr);
+            script = parser.parse(info, reader, scope, registers);
+            if (cached) {
+                cache.put(expr, script);
             }
         }
         return script;
     }
 
     /**
-     * Creates a JexlInfo instance.
-     * @param fn url/file name
-     * @param l line number
-     * @param c column number
-     * @return a JexlInfo instance
-     */
-    protected JexlInfo createInfo(String fn, int l, int c) {
-        return new JexlInfo(fn, l, c);
-    }
-
-    /**
-     * Creates and fills up debugging information.
-     * <p>This gathers the class, method and line number of the first calling method
-     * not owned by JexlEngine, TemplateEngine or {Script,JexlExpression}Factory.</p>
-     * @return an Info if debug is set, null otherwise
-     */
-    protected JexlInfo jexlInfo() {
-        JexlInfo info = null;
-        if (debug) {
-            Throwable xinfo = new Throwable();
-            xinfo.fillInStackTrace();
-            StackTraceElement[] stack = xinfo.getStackTrace();
-            StackTraceElement se = null;
-            Class<?> clazz = getClass();
-            for (int s = 1; s < stack.length; ++s) {
-                se = stack[s];
-                String className = se.getClassName();
-                if (!className.equals(clazz.getName())) {
-                    // go deeper if called from JexlEngine or TemplateEngine
-                    if (className.equals(JexlEngine.class.getName())) {
-                        clazz = JexlEngine.class;
-                    } else if (className.equals(TemplateEngine.class.getName())) {
-                        clazz = TemplateEngine.class;
-                    } else {
-                        break;
-                    }
+     * Trims the source from front & ending spaces.
+     * @param str expression to clean
+     * @return trimmed expression ending in a semi-colon
+     */
+    protected String trimSource(CharSequence str) {
+        if (str != null) {
+            int start = 0;
+            int end = str.length();
+            if (end > 0) {
+                // trim front spaces
+                while (start < end && Character.isSpaceChar(str.charAt(start))) {
+                    ++start;
                 }
+                // trim ending spaces
+                while (end > 0 && Character.isSpaceChar(str.charAt(end - 1))) {
+                    --end;
+                }
+                return str.subSequence(start, end).toString();
             }
-            if (se != null) {
-                info = createInfo(se.getClassName() + "." + se.getMethodName(), se.getLineNumber(), 0);
-            }
+            return "";
         }
-        return info;
+        return null;
     }
 }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Sat May 12 06:52:21 2012
@@ -197,8 +197,7 @@ public class Interpreter extends ParserV
      * @return the left, right or parent node
      */
     protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) {
-        if (xrt instanceof ArithmeticException
-                && (Object) JexlException.NULL_OPERAND == xrt.getMessage()) {
+        if (xrt instanceof JexlArithmetic.NullOperand) {
             if (left == null) {
                 return node.jjtGetChild(0);
             }
@@ -578,8 +577,6 @@ public class Interpreter extends ParserV
                 }
             }
             return result;
-        } catch (JexlException error) {
-            throw error;
         } catch (ArithmeticException xrt) {
             throw new JexlException(node.jjtGetChild(n), "if error", xrt);
         }
@@ -1275,6 +1272,7 @@ public class Interpreter extends ParserV
      * @param node    the method node
      * @param bean    the bean this method should be invoked upon
      * @param functor the object carrying the method or function
+     * @param argNode the node carrying the arguments
      * @return the result of the method invocation
      */
     private Object call(JexlNode node, Object bean, Object functor, ASTArguments argNode) {

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java Sat May 12 06:52:21 2012
@@ -16,40 +16,64 @@
  */
 package org.apache.commons.jexl3.internal;
 
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
 import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.parser.ASTJexlScript;
 
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
 /**
  * <p>A JexlScript implementation.</p>
  * @since 1.1
  */
 public class Script implements JexlScript {
-    /** The engine for this expression. */
+    /**
+     * The engine for this expression.
+     */
     protected final Engine jexl;
     /**
      * Original expression stripped from leading & trailing spaces.
      */
-    protected final String expression;
+    protected final String source;
     /**
      * The resulting AST we can interpret.
      */
     protected final ASTJexlScript script;
+    /**
+     * The engine version (as class loader change count) that last evaluated this script.
+     */
+    protected int version;
 
     /**
      * Do not let this be generally instantiated with a 'new'.
      *
      * @param engine the interpreter to evaluate the expression
-     * @param expr the expression.
-     * @param ref the parsed expression.
+     * @param expr   the expression source.
+     * @param ref    the parsed expression.
      */
     protected Script(Engine engine, String expr, ASTJexlScript ref) {
         jexl = engine;
-        expression = expr;
+        source = expr;
         script = ref;
+        version = jexl.getUberspect().getVersion();
+    }
+
+    /**
+     * Checks that this script cached methods (wrt introspection) matches the engine version.
+     * <p>
+     * If the engine class loader has changed since we last evaluated this script, the script local cache
+     * is invalidated to drop references to obsolete methods. It is not strictly necessary since the tryExecute
+     * will fail because the class wont match but it seems cleaner nevertheless.
+     * </p>
+     */
+    protected void checkCacheVersion() {
+        int uberVersion = jexl.getUberspect().getVersion();
+        if (version != uberVersion) {
+            script.clearCache();
+            version = uberVersion;
+        }
     }
 
     @Override
@@ -57,6 +81,7 @@ public class Script implements JexlScrip
         if (script.jjtGetNumChildren() < 1) {
             return null;
         }
+        checkCacheVersion();
         Scope.Frame frame = script.createFrame((Object[]) null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script.jjtGetChild(0));
@@ -66,27 +91,38 @@ public class Script implements JexlScrip
     public String dump() {
         Debugger debug = new Debugger();
         boolean d = debug.debug(script);
-        return debug.data() + (d ? " /*" + debug.start() + ":" + debug.end() + "*/" : "/*?:?*/ ");
+        return debug.toString() + (d ? " /*" + debug.start() + ":" + debug.end() + "*/" : "/*?:?*/ ");
     }
 
+    /**
+     * Gets this script original script source.
+     * @return the contents of the input source as a String.
+     */
     @Override
-    public String getExpression() {
-        return expression;
+    public String getSourceText() {
+        return source;
     }
 
+    /**
+     * Gets a string representation of this script underlying AST.
+     * @return the script as text
+     */
     @Override
-    public String toString() {
-        String expr = getExpression();
-        return expr == null ? "" : expr;
+    public String getParsedText() {
+        Debugger debug = new Debugger();
+        boolean d = debug.debug(script);
+        return debug.toString();
     }
 
-    /**
-     * Provide a string representation of this expression.
-     * @return the expression or blank if it's null.
-     */
     @Override
-    public String getText() {
-        return toString();
+    public String toString() {
+        CharSequence src = source;
+        if (src == null) {
+            Debugger debug = new Debugger();
+            boolean d = debug.debug(script);
+            src = debug.toString();
+        }
+        return src == null ? "/*no source*/" : src.toString();
     }
 
     /**
@@ -94,6 +130,7 @@ public class Script implements JexlScrip
      */
     @Override
     public Object execute(JexlContext context) {
+        checkCacheVersion();
         Scope.Frame frame = script.createFrame((Object[]) null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script);
@@ -104,7 +141,8 @@ public class Script implements JexlScrip
      */
     @Override
     public Object execute(JexlContext context, Object... args) {
-        Scope.Frame frame = script.createFrame(args != null && args.length > 0? args : null);
+        checkCacheVersion();
+        Scope.Frame frame = script.createFrame(args != null && args.length > 0 ? args : null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script);
     }
@@ -130,7 +168,7 @@ public class Script implements JexlScrip
 
     /**
      * Gets this script variables.
-     * <p>Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as 
+     * <p>Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as
      * a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.</p>
      * @return the variables or null
      */
@@ -156,7 +194,7 @@ public class Script implements JexlScrip
      * <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
      * <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
      * @param context the context
-     * @param args the script arguments
+     * @param args    the script arguments
      * @return the callable
      */
     @Override
@@ -165,9 +203,11 @@ public class Script implements JexlScrip
         return new Callable<Object>() {
             /** Use interpreter as marker for not having run. */
             private Object result = interpreter;
+
             @Override
             public Object call() throws Exception {
                 if (result == interpreter) {
+                    checkCacheVersion();
                     result = interpreter.interpret(script);
                 }
                 return result;

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=1337453&r1=1337452&r2=1337453&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 Sat May 12 06:52:21 2012
@@ -500,7 +500,7 @@ public final class TemplateEngine extend
         @Override
         protected TemplateExpression prepare(Interpreter interpreter) {
             String value = interpreter.interpret(node).toString();
-            JexlNode dnode = jexl.parse(value, jexl.isDebug() ? node.jexlInfo() : null, null);
+            JexlNode dnode = jexl.parse(jexl.isDebug() ? node.jexlInfo() : null, value, null, false);
             return new ImmediateExpression(value, dnode, this);
         }
 
@@ -742,9 +742,10 @@ public final class TemplateEngine extend
                 case IMMEDIATE1: // ${...
                     if (c == '}') {
                         // materialize the immediate expr
+                        String src = strb.toString();
                         TemplateExpression iexpr = new ImmediateExpression(
-                                strb.toString(),
-                                jexl.parse(strb, null, scope),
+                                src,
+                                jexl.parse(null, src, scope, false),
                                 null);
                         builder.add(iexpr);
                         strb.delete(0, Integer.MAX_VALUE);
@@ -777,16 +778,17 @@ public final class TemplateEngine extend
                             inner -= 1;
                         } else {
                             // materialize the nested/deferred expr
+                            String src = strb.toString();
                             TemplateExpression dexpr;
                             if (nested) {
                                 dexpr = new NestedExpression(
                                         expr.substring(inested, i + 1),
-                                        jexl.parse(strb, null, scope),
+                                        jexl.parse(null, src, scope, false),
                                         null);
                             } else {
                                 dexpr = new DeferredExpression(
                                         strb.toString(),
-                                        jexl.parse(strb, null, scope),
+                                        jexl.parse(null, src, scope, false),
                                         null);
                             }
                             builder.add(dexpr);
@@ -915,7 +917,7 @@ public final class TemplateEngine extend
                 }
             }
             // createExpression the script
-            script = jexl.parse(strb.toString(), null, scope);
+            script = jexl.parse(null, strb.toString(), scope, false);
             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) {

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java Sat May 12 06:52:21 2012
@@ -65,7 +65,7 @@ public final class Introspector {
     }
     /** The cache-miss marker for the constructors map. */
     private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
-    
+
     /** the logger. */
     protected final Log rlog;
     /**
@@ -92,10 +92,11 @@ public final class Introspector {
     /**
      * Create the introspector.
      * @param log the logger to use
+     * @param cloader the class loader
      */
-    public Introspector(Log log) {
+    public Introspector(Log log, ClassLoader cloader) {
         this.rlog = log;
-        loader = getClass().getClassLoader();
+        loader = cloader;
     }
 
     /**
@@ -196,8 +197,6 @@ public final class Introspector {
         return classMap.getMethods(methodName);
     }
 
-
-
     /**
      * Gets the constructor defined by the <code>MethodKey</code>.
      *
@@ -217,7 +216,7 @@ public final class Introspector {
      * or null if no unambiguous constructor could be found through introspection.
      */
     public Constructor<?> getConstructor(final Class<?> c, final MethodKey key) {
-        Constructor<?> ctor = null;
+        Constructor<?> ctor;
         try {
             lock.readLock().lock();
             ctor = constructorsMap.get(key);
@@ -317,7 +316,7 @@ public final class Introspector {
         }
         return classMap;
     }
-    
+
     /**
      * Sets the class loader used to solve constructors.
      * <p>Also cleans the constructors and methods caches.</p>

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java Sat May 12 06:52:21 2012
@@ -53,6 +53,22 @@ public final class SandboxUberspect impl
      * {@inheritDoc}
      */
     @Override
+    public void setClassLoader(ClassLoader loader) {
+        uberspect.setClassLoader(loader);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getVersion() {
+        return uberspect.getVersion();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public JexlMethod getConstructor(Object ctorHandle, Object[] args) {
         final String className;
         if (ctorHandle instanceof Class<?>) {
@@ -111,12 +127,10 @@ public final class SandboxUberspect impl
         return null;
 
     }
-
-    @Override
-    public void setClassLoader(ClassLoader loader) {
-        uberspect.setClassLoader(loader);
-    }
-
+    
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public Iterator<?> getIterator(Object obj) {
         return uberspect.getIterator(obj);

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java Sat May 12 06:52:21 2012
@@ -16,7 +16,9 @@
  */
 package org.apache.commons.jexl3.internal.introspection;
 
+import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 
 import java.lang.reflect.Method;
@@ -46,7 +48,11 @@ public class Uberspect implements JexlUb
     /** The logger to use for all warnings & errors. */
     protected final Log rlog;
     /** The soft reference to the introspector currently in use. */
-    private volatile SoftReference<Introspector> ref;
+    private volatile Reference<Introspector> ref;
+    /** The introspector version. */
+    private volatile int version;
+    /** The class loader weak reference; used to recreate the Introspector when necessary. */
+    private volatile Reference<ClassLoader> loader = null;
 
     /**
      * Creates a new Uberspect.
@@ -55,8 +61,10 @@ public class Uberspect implements JexlUb
     public Uberspect(Log runtimeLogger) {
         rlog = runtimeLogger;
         ref = new SoftReference<Introspector>(null);
+        version = 0;
+        loader = new WeakReference<ClassLoader>(getClass().getClassLoader());
     }
-    
+
     /**
      * Gets the current introspector base.
      * <p>If the reference has been collected, this method will recreate the underlying introspector.</p>
@@ -70,8 +78,15 @@ public class Uberspect implements JexlUb
             synchronized (this) {
                 intro = ref.get();
                 if (intro == null) {
-                    intro = new Introspector(rlog);
+                    ClassLoader cloader = loader.get();
+                    if (cloader == null) {
+                        // that would be really odd though...
+                        cloader = getClass().getClassLoader();
+                        loader = new WeakReference<ClassLoader>(cloader);
+                    }
+                    intro = new Introspector(rlog, cloader);
                     ref = new SoftReference<Introspector>(intro);
+                    version += 1;
                 }
             }
         }
@@ -80,8 +95,24 @@ public class Uberspect implements JexlUb
     // CSON: DoubleCheckedLocking
 
     @Override
-    public void setClassLoader(ClassLoader loader) {
-        base().setLoader(loader);
+    public void setClassLoader(ClassLoader nloader) {
+        synchronized (this) {
+            if (nloader == null) {
+                nloader = getClass().getClassLoader();
+            }
+            ClassLoader cloader = loader.get();
+            if (!nloader.equals(cloader)) {
+                Introspector intro = new Introspector(rlog, nloader);
+                loader = new WeakReference<ClassLoader>(nloader);
+                ref = new SoftReference<Introspector>(intro);
+                version += 1;
+            }
+        }
+    }
+
+    @Override
+    public int getVersion() {
+        return version;
     }
 
     /**
@@ -96,8 +127,8 @@ public class Uberspect implements JexlUb
     /**
      * Gets the field named by <code>key</code> for the class <code>c</code>.
      *
-     * @param c     Class in which the field search is taking place
-     * @param key   Name of the field being searched for
+     * @param c   Class in which the field search is taking place
+     * @param key Name of the field being searched for
      * @return a {@link java.lang.reflect.Field} or null if it does not exist or is not accessible
      * */
     public final Field getField(Class<?> c, String key) {
@@ -117,13 +148,13 @@ public class Uberspect implements JexlUb
      * Gets the method defined by <code>name</code> and
      * <code>params</code> for the Class <code>c</code>.
      *
-     * @param c Class in which the method search is taking place
-     * @param name Name of the method being searched for
+     * @param c      Class in which the method search is taking place
+     * @param name   Name of the method being searched for
      * @param params An array of Objects (not Classes) that describe the
-     *               the parameters
+     * the parameters
      *
      * @return a {@link java.lang.reflect.Method}
-     *  or null if no unambiguous method could be found through introspection.
+     * or null if no unambiguous method could be found through introspection.
      */
     public final Method getMethod(Class<?> c, String name, Object[] params) {
         return base().getMethod(c, new MethodKey(name, params));
@@ -132,11 +163,11 @@ public class Uberspect implements JexlUb
     /**
      * Gets the method defined by <code>key</code> and for the Class <code>c</code>.
      *
-     * @param c Class in which the method search is taking place
+     * @param c   Class in which the method search is taking place
      * @param key MethodKey of the method being searched for
      *
      * @return a {@link java.lang.reflect.Method}
-     *  or null if no unambiguous method could be found through introspection.
+     * or null if no unambiguous method could be found through introspection.
      */
     public final Method getMethod(Class<?> c, MethodKey key) {
         return base().getMethod(c, key);
@@ -153,7 +184,7 @@ public class Uberspect implements JexlUb
 
     /**
      * Gets all the methods with a given name from this map.
-     * @param c the class
+     * @param c          the class
      * @param methodName the seeked methods name
      * @return the array of methods
      */

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlSandbox.java Sat May 12 06:52:21 2012
@@ -54,7 +54,7 @@ import java.util.Set;
  * </ul>
  * </p>
  * <p>
- * Note that a JexlUberspect allways uses a copy of a JexlSandbox when built to avoid synchronization and/or
+ * Note that a JexlUberspect allways uses a copy of the JexlSandbox used to built it to avoid synchronization and/or
  * concurrent modifications at runtime.
  * </p>
  * @since 3.0
@@ -314,7 +314,7 @@ public final class JexlSandbox {
          * Creates a new permissions instance.
          * @param nread the read set
          * @param nwrite the write set
-         * @param nexecute the method set 
+         * @param nexecute the method set
          */
         Permissions(Names nread, Names nwrite, Names nexecute) {
             this.read = nread != null ? nread : WHITE_NAMES;

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java Sat May 12 06:52:21 2012
@@ -20,19 +20,26 @@ package org.apache.commons.jexl3.introsp
 import java.util.Iterator;
 
 /**
- * 'Federated' introspection/reflection interface to allow the introspection
- * behavior in JEXL to be customized.
- * 
+ * 'Federated' introspection/reflection interface to allow JEXL introspection
+ *  behavior to be customized.
+ *
  * @since 1.0
  */
 public interface JexlUberspect {
-    /** Sets the class loader to use when getting a constructor with
-     * a class name parameter.
+    /**
+     * Sets the class loader to use.
+     * <p>This increments the version.</p>
      * @param loader the class loader
      */
     void setClassLoader(ClassLoader loader);
 
     /**
+     * Gets this uberspect version.
+     * @return the class loader modification count
+     */
+    int getVersion();
+
+    /**
      * Returns a class constructor.
      * @param ctorHandle a class or class name
      * @param args constructor arguments
@@ -40,7 +47,7 @@ public interface JexlUberspect {
      * @since 3.0
      */
     JexlMethod getConstructor(Object ctorHandle, Object[] args);
-    
+
     /**
      * Returns a JexlMethod.
      * @param obj the object

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/package.html?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/package.html (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/package.html Sat May 12 06:52:21 2012
@@ -194,27 +194,27 @@
         <h4>JexlExpression </h4>
         <p>
             These are the most basic form of JexlEngine expressions and only allow for a single command
-            to be executed and its result returned. If you try to use multiple commands, it ignores 
-            everything after the first semi-colon and just returns the result from 
-            the first command. 
+            to be executed and its result returned. If you try to use multiple commands, it ignores
+            everything after the first semi-colon and just returns the result from
+            the first command.
         </p>
         <h4>JexlScript</h4>
         <p>
-            This allows you to put multiple commands in the expression and you can 
+            This allows you to put multiple commands in the expression and you can
             use variable assignments, loops, calculations, etc. More or less what can be achieved in Shell or
             JavaScript at its basic level. The result from the last command is returned from the script.
         </p>
-        <h4>JxltEngine.UnifiedExpression</h4> 
+        <h4>JxltEngine.UnifiedExpression</h4>
         <p>
             These are ideal to produce "one-liner" text, like a 'toString()' on steroids.
-            To get a calculation you use the EL-like syntax 
-            as in ${someVariable}. The expression that goes between the brackets 
-            behaves like a JexlScript, not an expression. You can use semi-colons to 
-            execute multiple commands and the result from the last command is 
+            To get a calculation you use the EL-like syntax
+            as in ${someVariable}. The expression that goes between the brackets
+            behaves like a JexlScript, not an expression. You can use semi-colons to
+            execute multiple commands and the result from the last command is
             returned from the script. You also have the ability to use a 2-pass evaluation using
             the #{someScript} syntax.
         </p>
-        <h4>JxltEngine.Template</h4> 
+        <h4>JxltEngine.Template</h4>
         <p>
             These produce text documents. Each line beginning with '$$' (as a default) is
             considered JEXL code and all others considered as JxltEngine.UnifiedExpression.
@@ -346,6 +346,8 @@
             be the case if you wanted '+' to operate on arrays; you'd need to derive JexlArithmetic and
             implement your own version of Add.
             Note however that you can not change the operator precedence.
+
+
         </p>
 
         <h2><a name="extension">Extending JEXL</a></h2>

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java Sat May 12 06:52:21 2012
@@ -39,6 +39,19 @@ public class ASTJexlScript extends JexlN
     }
 
     /**
+     * Coerce this script as an expression (ie only one child) if necessary.
+     * @return true if the script was coerced, false otherwise
+     */
+    public boolean toExpression() {
+        if (children.length > 1) {
+            children = new JexlNode[]{children[0]};
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
      * Sets this script scope.
      * @param theScope the scope
      */

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java Sat May 12 06:52:21 2012
@@ -17,6 +17,9 @@
 package org.apache.commons.jexl3.parser;
 
 import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.introspection.JexlMethod;
+import org.apache.commons.jexl3.introspection.JexlPropertyGet;
+import org.apache.commons.jexl3.introspection.JexlPropertySet;
 
 /**
  * Base class for parser nodes - holds an 'image' of the token for later use.
@@ -71,6 +74,24 @@ public abstract class JexlNode extends S
     }
 
     /**
+     * Clears any cached value of type JexlProperty{G,S}et or JexlMethod.
+     * <p>This is called when the engine detects the evaluation of a script occurs with a class loader
+     * different that the one that created it.</p>
+     */
+    public void clearCache() {
+        if (value instanceof JexlPropertyGet
+                || value instanceof JexlPropertySet
+                || value instanceof JexlMethod) {
+            value = null;
+        }
+        if (children != null) {
+            for (int n = 0; n < children.length; ++n) {
+                children[n].clearCache();
+            }
+        }
+    }
+
+    /**
      * Whether this node is a constant node Its value can not change after the first evaluation and can be cached
      * indefinitely.
      *
@@ -114,8 +135,8 @@ public abstract class JexlNode extends S
         }
         int nc = this.jjtGetNumChildren() - 1;
         if (nc >= 0) {
-           JexlNode last = this.jjtGetChild(this.jjtGetNumChildren() - 1);
-           return last.isLeftValue();
+            JexlNode last = this.jjtGetChild(this.jjtGetNumChildren() - 1);
+            return last.isLeftValue();
         }
         if (parent instanceof ASTReference || parent instanceof ASTArrayAccess) {
             return true;

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java Sat May 12 06:52:21 2012
@@ -161,7 +161,7 @@ public abstract class JexlParser extends
             } else {
                 dbgInfo = node.jexlInfo();
             }
-            throw new JexlException.Parsing(dbgInfo, "Ambiguous statement, missing ';' between expressions", null);
+            throw new JexlException.Parsing(dbgInfo, "Ambiguous statement, missing ';' between expressions");
         } else if (node instanceof ASTAssignment) {
             JexlNode lv = node.jjtGetChild(0);
             if (!lv.isLeftValue()) {
@@ -172,7 +172,7 @@ public abstract class JexlParser extends
                 } else {
                     dbgInfo = node.jexlInfo();
                 }
-                throw new JexlException.Parsing(dbgInfo, "Invalid assignment expression", null);
+                throw new JexlException.Parsing(dbgInfo, "Invalid assignment expression");
             }
         }
     }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt Sat May 12 06:52:21 2012
@@ -33,31 +33,40 @@ package org.apache.commons.jexl3.parser;
 
 import java.io.Reader;
 import org.apache.commons.jexl3.JexlInfo;
-import org.apache.commons.jexl3.internal.Engine;
+import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.internal.Scope;
 
-public class Parser extends JexlParser
+public final class Parser extends JexlParser
 {
-    public boolean ALLOW_REGISTERS = false;
+    private boolean ALLOW_REGISTERS = false;
 
-    public ASTJexlScript parse(Reader reader, Scope scope, JexlInfo info)
-        throws ParseException {
-        /*
-         * If registers are allowed, the default parser state has to be REGISTERS.
-         */
-        if (ALLOW_REGISTERS) {
-            token_source.defaultLexState = REGISTERS;
+    public ASTJexlScript parse(JexlInfo info, Reader reader, Scope scope, boolean registers) {
+        try {
+            // If registers are allowed, the default parser state has to be REGISTERS.
+            if (registers || ALLOW_REGISTERS) {
+                token_source.defaultLexState = REGISTERS;
+            }
+            // lets do the 'Unique Init' in here to be safe - it's a pain to remember
+            ReInit(reader);
+            frame = scope;
+            ASTJexlScript script = JexlScript(scope);
+            script.value = info;
+            return script;
+        } catch (TokenMgrError xtme) {
+            throw new JexlException.Tokenization(info, xtme).clean();
+        } catch (ParseException xparse) {
+            throw new JexlException.Parsing(info, xparse).clean();
+        } finally {
+            frame = null;
+            token_source.defaultLexState = DEFAULT;
         }
-        ReInit(reader);
-        /*
-         *  lets do the 'Unique Init' in here to be
-         *  safe - it's a pain to remember
-         */
-
-        frame = scope;
-        ASTJexlScript script = JexlScript(scope);
-        script.value = info;
-        return script;
+    }
+
+    /**
+     * Internal, for debug purpose only.
+     */
+    public void allowRegisters(boolean registers) {
+        ALLOW_REGISTERS = registers;
     }
 
 }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngine.java Sat May 12 06:52:21 2012
@@ -17,6 +17,7 @@
 
 package org.apache.commons.jexl3.scripting;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Reader;
@@ -32,7 +33,6 @@ import javax.script.ScriptEngineFactory;
 import javax.script.ScriptException;
 import javax.script.SimpleBindings;
 
-import org.apache.commons.jexl3.internal.Engine;
 import org.apache.commons.jexl3.JexlBuilder;
 import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlEngine;
@@ -47,7 +47,7 @@ import org.apache.commons.logging.LogFac
  * This implementation gives access to both ENGINE_SCOPE and GLOBAL_SCOPE bindings.
  * When a JEXL script accesses a variable for read or write,
  * this implementation checks first ENGINE and then GLOBAL scope.
- * The first one found is used. 
+ * The first one found is used.
  * If no variable is found, and the JEXL script is writing to a variable,
  * it will be stored in the ENGINE scope.
  * </p>
@@ -78,10 +78,10 @@ public class JexlScriptEngine extends Ab
 
     /** The factory which created this instance. */
     private final ScriptEngineFactory parentFactory;
-    
+
     /** The JEXL EL engine. */
     private final JexlEngine jexlEngine;
-    
+
     /**
      * Default constructor.
      * <p>
@@ -177,7 +177,7 @@ public class JexlScriptEngine extends Ab
 
     /**
      * Create a scripting engine using the supplied factory.
-     * 
+     *
      * @param factory the factory which created this instance.
      * @throws NullPointerException if factory is null
      */
@@ -222,6 +222,7 @@ public class JexlScriptEngine extends Ab
     }
 
     /** {@inheritDoc} */
+    @Override
     public ScriptEngineFactory getFactory() {
         return parentFactory;
     }
@@ -250,21 +251,33 @@ public class JexlScriptEngine extends Ab
     }
 
     /**
-     * Reads a script.
-     * @param script the script reader
-     * @return the script as a string
-     * @throws ScriptException if an exception occurs during read
+     * Read from a reader into a local buffer and return a String with
+     * the contents of the reader.
+     * @param scriptReader to be read.
+     * @return the contents of the reader as a String.
+     * @throws ScriptException on any error reading the reader.
      */
-    private String readerToString(final Reader script) throws ScriptException {
+    private static String readerToString(Reader scriptReader) throws ScriptException {
+        StringBuilder buffer = new StringBuilder();
+        BufferedReader reader;
+        if (scriptReader instanceof BufferedReader) {
+            reader = (BufferedReader) scriptReader;
+        } else {
+            reader = new BufferedReader(scriptReader);
+        }
         try {
-           return Engine.readerToString(script);
+            String line;
+            while ((line = reader.readLine()) != null) {
+                buffer.append(line).append('\n');
+            }
+            return buffer.toString();
         } catch (IOException e) {
             throw new ScriptException(e);
         }
     }
 
     /**
-     * Holds singleton JexlScriptEngineFactory (IODH). 
+     * Holds singleton JexlScriptEngineFactory (IODH).
      */
     private static class FactorySingletonHolder {
         /** non instantiable. */
@@ -347,9 +360,9 @@ public class JexlScriptEngine extends Ab
         /** {@inheritDoc} */
         @Override
         public String toString() {
-            return script.getText();
+            return script.getSourceText();
         }
-        
+
         /** {@inheritDoc} */
         @Override
         public Object eval(final ScriptContext context) throws ScriptException {
@@ -362,7 +375,7 @@ public class JexlScriptEngine extends Ab
                 throw new ScriptException(e.toString());
             }
         }
-        
+
         /** {@inheritDoc} */
         @Override
         public ScriptEngine getEngine() {

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/scripting/JexlScriptEngineFactory.java Sat May 12 06:52:21 2012
@@ -23,7 +23,6 @@ import java.util.List;
 
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineFactory;
-import org.apache.commons.jexl3.internal.Engine;
 import org.apache.commons.jexl3.parser.StringParser;
 
 /**
@@ -137,7 +136,7 @@ public class JexlScriptEngineFactory imp
     public String getProgram(String... statements) {
         StringBuilder sb = new StringBuilder();
         for(String statement : statements){
-            sb.append(Engine.cleanExpression(statement));
+            sb.append(statement.trim());
             if (!statement.endsWith(";")){
                 sb.append(';');
             }

Modified: commons/proper/jexl/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/site.xml?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/site.xml (original)
+++ commons/proper/jexl/trunk/src/site/site.xml Sat May 12 06:52:21 2012
@@ -26,6 +26,7 @@
         <menu name="Commons&#xA0;Jexl">
             <item name="Overview"                href="index.html" />
             <item name="3.0&#xA0;Javadoc"        href="apidocs/index.html"/>
+            <item name="2.1&#xA0;Javadoc"        href="apidocs-2.1/index.html"/>
             <item name="1.1&#xA0;Javadoc"        href="apidocs-1.1/index.html"/>
             <item name="Download"                href="download_jexl.cgi"/>
             <item name="Reference"               href="reference/index.html"/>

Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Sat May 12 06:52:21 2012
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<!-- 
+<!--
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -8,9 +8,9 @@
  * 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.
@@ -25,83 +25,91 @@
         <author email="dev@commons.apache.org">Commons Developers</author>
     </properties>
     <body>
-        <release version="3.0" date="unreleased">
+        <release version="3.0" date="2012-06-01">
             <action dev="henrib" type="add" issue="JEXL-128" due-to="Matteo Trotta">
-            ObjectContext&lt;&gt; should implement NamespaceResolver
+                ObjectContext should implement NamespaceResolver
             </action>
             <action dev="henrib" type="add" issue="JEXL-127">
-            Allow the creation of functions
-            </action>
-            <action dev="henrib" type="add" issue="JEXL-126">
-            Decimal numbers literals should be 'double' by default (instead of 'float')
+                Allow the creation of functions
             </action>
             <action dev="henrib" type="add" issue="JEXL-123">
-            Redesign API for stability
+                Redesign API for stability
             </action>
             <action dev="henrib" type="add" issue="JEXL-122">
-            Move JEXL from org.apache.comms.jexl2 to org.apache.commons.jexl3
+                Move JEXL from org.apache.comms.jexl2 to org.apache.commons.jexl3
+            </action>
+        </release>
+        <release version="2.1.2" date="2012-05-30">
+            <action dev="henrib" type="fix" issue="JEXL-131" due-to="Clay Bruce">
+                UnifiedJexl parsing may fail with NPE
+            </action>
+            <action dev="henrib" type="fix" issue="JEXL-130" due-to="William Bakker">
+                Ternary Conditional fails for Object values
+            </action>
+            <action dev="henrib" type="fix" issue="JEXL-126" due-to="Grace">
+                Decimal numbers literals should be 'double' by default (instead of 'float')
             </action>
         </release>
-        <release version="2.1.1" date="2011-11-24">
-            <action dev="henrib" type="fix" issue="JEXL-121">
-            Array parameters to methods don't work anymore
+        <release version="2.1.1" date="2011-12-24">
+            <action dev="henrib" type="fix" issue="JEXL-124">
+                Array parameters to methods don't work anymore (regression)
             </action>
         </release>
         <release version="2.1" date="2011-13-11">
             <action dev="henrib" type="add" issue="JEXL-121">
-            Add simple template features
+                Add simple template features
             </action>
             <action dev="henrib" type="fix" issue="JEXL-120" due-to="Lukas Krecan">
-            Make ParseException work in sandboxed environment
+                Make ParseException work in sandboxed environment
             </action>
             <action dev="henrib" type="add" issue="JEXL-119">
-            Allow indexed properties container resolution in expressions
+                Allow indexed properties container resolution in expressions
             </action>
             <action dev="henrib" type="add" issue="JEXL-118" due-to="Max Tardiveau">
-            Provide an IN operator: =~ / match operator extended to provide IN behavior (!~ as NOT IN)
+                Provide an IN operator: =~ / match operator extended to provide IN behavior (!~ as NOT IN)
             </action>
             <action dev="henrib" type="add" issue="JEXL-116" due-to="Sarel Botha">
-            Add control over classes, methods, constructors and properties allowed in scripts
+                Add control over classes, methods, constructors and properties allowed in scripts
             </action>
             <action dev="henrib" type="add" issue="JEXL-115" due-to="Sarel Botha">
-            Add support for asynchronous script execution and cancellation
+                Add support for asynchronous script execution and cancellation
             </action>
             <action dev="henrib" type="add" issue="JEXL-114">
-            Allow scripts to create local variables // Add return keyword
+                Allow scripts to create local variables // Add return keyword
             </action>
             <action dev="henrib" type="add" issue="JEXL-113" due-to="Max Tardiveau">
-            Add functions to extract which variables, parameters and local variables are used to evaluate a script
+                Add functions to extract which variables, parameters and local variables are used to evaluate a script
             </action>
             <action dev="henrib" type="fix" issue="JEXL-112" due-to="sebb">
-            Cannot parse Integer.MIN_VALUE.
+                Cannot parse Integer.MIN_VALUE.
             </action>
             <action dev="henrib" type="fix" issue="JEXL-24" due-to="freish">
-            Support Long for integer literal instead of Integers.
+                Support Long for integer literal instead of Integers.
             </action>
             <action dev="henrib" type="add">
-            Added ObjectContext that wraps an object as JexlContext and added JexlContext as source to solve
-            top-level namespace functions.
+                Added ObjectContext that wraps an object as JexlContext and added JexlContext as source to solve
+                top-level namespace functions.
             </action>
             <action dev="henrib" type="fix" issue="JEXL-111" due-to="freish">
-            expression execute error depending on numeric type
+                expression execute error depending on numeric type
             </action>
             <action dev="henrib" type="add" issue="JEXL-83" due-to="sebb">
-            Make JexlArithmetic immutable (and threadsafe); also added a mutable JexlThreadedArithmetic.
+                Make JexlArithmetic immutable (and threadsafe); also added a mutable JexlThreadedArithmetic.
             </action>
             <action dev="henrib" type="fix" issue="JEXL-108" due-to="Cristian Lorenzetto">
-            Parsing error if i define a empty literal array/map
+                Parsing error if i define a empty literal array/map
             </action>
             <action dev="henrib" type="fix" issue="JEXL-107">
-            Literals and parenthesized expressions can not be used as references
+                Literals and parenthesized expressions can not be used as references
             </action>
             <action dev="henrib" type="fix" issue="JEXL-106" due-to="Michal Sabol">
-            When divide two BigDecimal values in an expression it results in java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
+                When divide two BigDecimal values in an expression it results in java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
             </action>
             <action dev="henrib" type="add" issue="JEXL-105" due-to="Cary Thompson">Array literals are considered constant even when they are not.</action>
             <action dev="henrib" type="fix" issue="JEXL-104" due-to="Andreas Haufler">NPE in JexlArithmetic when an Array-Expression containing a null is used.</action>
             <action dev="henrib" type="add" issue="JEXL-102" due-to="sebb">Add "jexl2" as a supported name</action>
             <action dev="henrib" type="add" issue="JEXL-101" due-to="Tobias Shulte">
-            Vararg methods where the first argument is no vararg can not be called with only the fixed parameters given
+                Vararg methods where the first argument is no vararg can not be called with only the fixed parameters given
             </action>
         </release>
         <release version="2.0.1" date="2010-03-28">

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java Sat May 12 06:52:21 2012
@@ -156,6 +156,7 @@ public class AssignTest extends JexlTest
 
     public void testUtil() throws Exception {
         Quux quux = JEXL.newInstance(Quux.class, "xuuq", Integer.valueOf(100));
+        assertNotNull(quux);
         JEXL.setProperty(quux, "froboz.value", Integer.valueOf(100));
         Object o = JEXL.getProperty(quux, "froboz.value");
         assertEquals("Result is not 100", new Integer(100), o);

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=1337453&r1=1337452&r2=1337453&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 Sat May 12 06:52:21 2012
@@ -287,24 +287,24 @@ public class IssuesTest extends JexlTest
         JexlScript jscript;
 
         jscript = jexl.createScript("dummy.hashCode()");
-        assertEquals(jscript.getText(), null, jscript.execute(ctxt)); // OK
+        assertEquals(jscript.getSourceText(), null, jscript.execute(ctxt)); // OK
 
         ctxt.set("dummy", "abcd");
-        assertEquals(jscript.getText(), Integer.valueOf("abcd".hashCode()), jscript.execute(ctxt)); // OK
+        assertEquals(jscript.getSourceText(), Integer.valueOf("abcd".hashCode()), jscript.execute(ctxt)); // OK
 
         jscript = jexl.createScript("dummy.hashCode");
-        assertEquals(jscript.getText(), null, jscript.execute(ctxt)); // OK
+        assertEquals(jscript.getSourceText(), null, jscript.execute(ctxt)); // OK
 
         JexlExpression jexpr;
         vars.clear();
         jexpr = jexl.createExpression("dummy.hashCode()");
-        assertEquals(jexpr.getExpression(), null, jexpr.evaluate(ctxt)); // OK
+        assertEquals(jexpr.toString(), null, jexpr.evaluate(ctxt)); // OK
 
         ctxt.set("dummy", "abcd");
-        assertEquals(jexpr.getExpression(), Integer.valueOf("abcd".hashCode()), jexpr.evaluate(ctxt)); // OK
+        assertEquals(jexpr.toString(), Integer.valueOf("abcd".hashCode()), jexpr.evaluate(ctxt)); // OK
 
         jexpr = jexl.createExpression("dummy.hashCode");
-        assertEquals(jexpr.getExpression(), null, jexpr.evaluate(ctxt)); // OK
+        assertEquals(jexpr.toString(), null, jexpr.evaluate(ctxt)); // OK
     }
 
     // JEXL-73
@@ -610,10 +610,10 @@ public class IssuesTest extends JexlTest
         String[] names = {"foo"};
         Object value;
         JexlContext context = new MapContext();
-        value = jexl.createScript("foo + 2", null, names).execute(context, 40);
+        value = jexl.createScript("foo + 2", names).execute(context, 40);
         assertEquals(42, value);
         context.set("frak.foo", -40);
-        value = jexl.createScript("frak.foo - 2", null, names).execute(context, 40);
+        value = jexl.createScript("frak.foo - 2", names).execute(context, 40);
         assertEquals(-42, value);
     }
 
@@ -780,4 +780,134 @@ public class IssuesTest extends JexlTest
         assertEquals(sevendot475, (BigDecimal) evaluate);
     }
 
+    public static class Arithmetic132 extends JexlArithmetic {
+        public Arithmetic132() {
+            super(false);
+        }
+
+        protected double divideZero(BigDecimal x) {
+            int ls = x.signum();
+            if (ls < 0) {
+                return Double.NEGATIVE_INFINITY;
+            } else if (ls > 0) {
+                return Double.POSITIVE_INFINITY;
+            } else {
+                return Double.NaN;
+            }
+        }
+
+        protected double divideZero(BigInteger x) {
+            int ls = x.signum();
+            if (ls < 0) {
+                return Double.NEGATIVE_INFINITY;
+            } else if (ls > 0) {
+                return Double.POSITIVE_INFINITY;
+            } else {
+                return Double.NaN;
+            }
+        }
+
+        @Override
+        public Object divide(Object left, Object right) {
+            if (left == null && right == null) {
+                return controlNullNullOperands();
+            }
+            // if either are bigdecimal use that type
+            if (left instanceof BigDecimal || right instanceof BigDecimal) {
+                BigDecimal l = toBigDecimal(left);
+                BigDecimal r = toBigDecimal(right);
+                if (BigDecimal.ZERO.equals(r)) {
+                    return divideZero(l);
+                }
+                BigDecimal result = l.divide(r, getMathContext());
+                return narrowBigDecimal(left, right, result);
+            }
+            // if either are floating point (double or float) use double
+            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+                double l = toDouble(left);
+                double r = toDouble(right);
+                return new Double(l / r);
+            }
+            // otherwise treat as integers
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            if (BigInteger.ZERO.equals(r)) {
+                return divideZero(l);
+            }
+            BigInteger result = l.divide(r);
+            return narrowBigInteger(left, right, result);
+        }
+
+        @Override
+        public Object mod(Object left, Object right) {
+            if (left == null && right == null) {
+                return controlNullNullOperands();
+            }
+            // if either are bigdecimal use that type
+            if (left instanceof BigDecimal || right instanceof BigDecimal) {
+                BigDecimal l = toBigDecimal(left);
+                BigDecimal r = toBigDecimal(right);
+                if (BigDecimal.ZERO.equals(r)) {
+                    return divideZero(l);
+                }
+                BigDecimal remainder = l.remainder(r, getMathContext());
+                return narrowBigDecimal(left, right, remainder);
+            }
+            // if either are floating point (double or float) use double
+            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+                double l = toDouble(left);
+                double r = toDouble(right);
+                return new Double(l % r);
+            }
+            // otherwise treat as integers
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            BigInteger result = l.mod(r);
+            if (BigInteger.ZERO.equals(r)) {
+                return divideZero(l);
+            }
+            return narrowBigInteger(left, right, result);
+        }
+    }
+
+    JexlEngine createJexl132() {
+        Map<String, Object> ns = new HashMap<String, Object>();
+        ns.put("math", Math.class);
+        return new JexlBuilder().arithmetic(new Arithmetic132()).namespaces(ns).create();
+
+    }
+
+    public void test132a() throws Exception {
+        JexlEngine jexl = createJexl132();
+        String expr = "1/0";
+
+        Object evaluate = jexl.createExpression(expr).evaluate(null);
+        assertTrue(Double.isInfinite((Double)evaluate));
+
+        expr = "-1/0";
+        evaluate = jexl.createExpression(expr).evaluate(null);
+        assertTrue(Double.isInfinite((Double)evaluate));
+    }
+
+
+    public void test132b() throws Exception {
+        JexlEngine jexl = createJexl132();
+        String expr = "1.0/0.0";
+
+        Object evaluate = jexl.createExpression(expr).evaluate(null);
+        assertTrue(Double.isInfinite((Double)evaluate));
+
+        expr = "-1.0/0";
+        evaluate = jexl.createExpression(expr).evaluate(null);
+        assertTrue(Double.isInfinite((Double)evaluate));
+    }
+
+    public void test132c() throws Exception {
+        JexlEngine jexl = createJexl132();
+        String expr = "math:abs(-42)";
+        
+        Object evaluate = jexl.createExpression(expr).evaluate(null);
+        assertEquals(42, evaluate);
+    }
+
 }

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java Sat May 12 06:52:21 2012
@@ -30,6 +30,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.jexl3.parser.ASTJexlScript;
 import org.apache.commons.jexl3.parser.Parser;
 
 /**
@@ -649,7 +650,7 @@ public class JexlTest extends JexlTestCa
         Foo foo = new Foo();
         jc.set("foo", foo);
         Parser parser = new Parser(new StringReader(";"));
-        parser.parse(new StringReader("aString = 'World';"), null, null);
+        parser.parse(null, new StringReader("aString = 'World';"), null, false);
 
         assertExpression(jc, "hello = 'world'", "world");
         assertEquals("hello variable not changed", "world", jc.get("hello"));

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java Sat May 12 06:52:21 2012
@@ -39,8 +39,8 @@ public class LambdaTest extends JexlTest
         JexlEngine jexl = new Engine();
         JexlScript s = jexl.createScript("function(x) { x + x }");
         JexlScript fs = (JexlScript) s.execute(null);
-        String fsstr = fs.getExpression();
-        assertEquals("(x)->{ x + x; };", fsstr);
+        String fsstr = fs.toString();
+        assertEquals("(x)->{ x + x; }", fsstr);
         assertEquals(42, fs.execute(null, 21));
         JexlScript s42 = jexl.createScript("s(21)");
         JexlEvalContext ctxt = new JexlEvalContext();

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java?rev=1337453&r1=1337452&r2=1337453&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java Sat May 12 06:52:21 2012
@@ -34,7 +34,7 @@ public class PropertyAccessTest extends 
     public PropertyAccessTest() {
         super("PropertyAccessTest");
     }
-    
+
     @Override
     public void setUp() {
         asserter = new Asserter(JEXL);
@@ -65,7 +65,7 @@ public class PropertyAccessTest extends 
             asserter.assertExpression("foo.0.'1'", i42);
         }
     }
-    
+
     public static class Container {
         String value0;
         int value1;
@@ -169,7 +169,7 @@ public class PropertyAccessTest extends 
         result = get1.execute(null, quux);
         assertEquals(24, result);
     }
-    
+
 
     public void testStringIdentifier() throws Exception {
         Map<String, String> foo = new HashMap<String, String>();
@@ -192,7 +192,7 @@ public class PropertyAccessTest extends 
 
         Debugger dbg = new Debugger();
         dbg.debug(e);
-        String dbgdata = dbg.data();
-        assertEquals("foo.'q u u x';", dbgdata);
+        String dbgdata = dbg.toString();
+        assertEquals("foo.'q u u x'", dbgdata);
     }
 }
\ No newline at end of file