commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hen...@apache.org
Subject svn commit: r1210927 [2/7] - 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...
Date Tue, 06 Dec 2011 14:19:36 GMT
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java Tue Dec  6 14:19:33 2011
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.jexl3;
 
+import org.apache.commons.jexl3.internal.Debugger;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.UndeclaredThrowableException;
 import org.apache.commons.jexl3.parser.JexlNode;
@@ -28,9 +29,9 @@ import org.apache.commons.jexl3.parser.T
  */
 public class JexlException extends RuntimeException {
     /** The point of origin for this exception. */
-    protected final transient JexlNode mark;
+    private final transient JexlNode mark;
     /** The debug info. */
-    protected final transient JexlInfo info;
+    private final transient JexlInfo info;
     /** A marker to use in NPEs stating a null operand error. */
     public static final String NULL_OPERAND = "jexl.null";
     /** Minimum number of characters around exception location. */
@@ -306,7 +307,7 @@ public class JexlException extends Runti
      * Thrown to return a value.
      * @since 3.0
      */
-    protected static class Return extends JexlException {
+    public static class Return extends JexlException {
         /** The returned value. */
         private final Object result;
 
@@ -316,7 +317,7 @@ public class JexlException extends Runti
          * @param msg the message
          * @param value the returned value
          */
-        protected Return(JexlNode node, String msg, Object value) {
+        public Return(JexlNode node, String msg, Object value) {
             super(node, msg);
             this.result = value;
         }
@@ -333,12 +334,12 @@ public class JexlException extends Runti
      * Thrown to cancel a script execution.
      * @since 3.0
      */
-    protected static class Cancel extends JexlException {
+    public static class Cancel extends JexlException {
         /**
          * Creates a new instance of Cancel.
          * @param node the node where the interruption was detected
          */
-        protected Cancel(JexlNode node) {
+        public Cancel(JexlNode node) {
             super(node, "execution cancelled", null);
         }
     }

Copied: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlExpression.java (from r1209060, commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Expression.java)
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlExpression.java?p2=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlExpression.java&p1=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Expression.java&r1=1209060&r2=1210927&rev=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Expression.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlExpression.java Tue Dec  6 14:19:33 2011
@@ -21,18 +21,18 @@ package org.apache.commons.jexl3;
 /**
  * Represents a single JEXL expression.
  * <p>
- * This simple interface provides access to the underlying expression through
- * {@link Expression#getExpression()}.
+ * This simple interface provides access to the underlying textual expression through
+ * {@link JexlExpression#getExpression()}.
  * </p>
  *
  * <p>
- * An expression is different than a script - it is simply a reference of
- * an expression.
+ * An expression is different than a script - it is simply a reference to
+ * a single expression.
  * </p>
  *
  * @since 1.0
  */
-public interface Expression {
+public interface JexlExpression {
     /**
      * Evaluates the expression with the variables contained in the
      * supplied {@link JexlContext}.
@@ -44,8 +44,7 @@ public interface Expression {
     Object evaluate(JexlContext context);
 
     /**
-     * Returns the JEXL expression this Expression was created with.
-     *
+     * Returns the JEXL expression this JexlExpression was created with.
      * @return The JEXL expression to be evaluated
      */
     String getExpression();

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java Tue Dec  6 14:19:33 2011
@@ -18,7 +18,7 @@ package org.apache.commons.jexl3;
 
 
 /**
- * Helper class to carry in info such as a url/file name, line and column for
+ * Helper class to carry information such as a url/file name, line and column for
  * debugging information reporting.
  */
 public class JexlInfo {
@@ -40,20 +40,9 @@ public class JexlInfo {
         line = l;
         column = c;
     }
-    
-    /**
-     * Denotes objects that can expose a JexlInfo instance.
-     */
-    public interface Handle {
-        /**
-         * Gets the associated JexlInfo instance.
-         * @return the info
-         */
-        JexlInfo jexlInfo();
-    }
 
     /**
-     * Formats this info in the form 'name&#064;line:column'.
+     * Formats this info in the form 'name&#064line:column'.
      * @return the formatted info
      */
     @Override
@@ -74,7 +63,7 @@ public class JexlInfo {
      * Gets the file/script/url name.
      * @return template name
      */
-    public String getName() {
+    public final String getName() {
         return name;
     }
 
@@ -82,7 +71,7 @@ public class JexlInfo {
      * Gets the line number.
      * @return line number.
      */
-    public int getLine() {
+    public final int getLine() {
         return line;
     }
 
@@ -90,7 +79,7 @@ public class JexlInfo {
      * Gets the column number.
      * @return the column.
      */
-    public int getColumn() {
+    public final int getColumn() {
         return column;
     }
 }

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfoHandle.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfoHandle.java?rev=1210927&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfoHandle.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfoHandle.java Tue Dec  6 14:19:33 2011
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Denotes objects that can expose a JexlInfo instance.
+ * @since 3.0
+ */
+public interface JexlInfoHandle {
+    /**
+     * Gets the associated JexlInfo instance.
+     * @return the info
+     */
+    JexlInfo jexlInfo();
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfoHandle.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlScript.java (from r1209060, commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Script.java)
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlScript.java?p2=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlScript.java&p1=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Script.java&r1=1209060&r2=1210927&rev=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Script.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlScript.java Tue Dec  6 14:19:33 2011
@@ -16,6 +16,10 @@
  */
 package org.apache.commons.jexl3;
 
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
 /**
  * <p>A JEXL Script.</p>
  * <p>A script is some valid JEXL syntax to be executed with
@@ -27,7 +31,7 @@ package org.apache.commons.jexl3;
  *  
  * @since 1.1
  */
-public interface Script {
+public interface JexlScript extends JexlExpression {
     /**
      * Executes the script with the variables contained in the
      * supplied {@link JexlContext}. 
@@ -47,7 +51,7 @@ public interface Script {
      * @param args the arguments
      * @return The result of this script, usually the result of 
      *      the last statement.
-     * @since 3.0
+     * @since 2.1
      */
     Object execute(JexlContext context, Object... args);
 
@@ -56,4 +60,48 @@ public interface Script {
      * @return The script to be executed.
      */
     String getText();
+
+    /**
+     * Gets this script parameters.
+     * @return the parameters or null
+     * @since 2.1
+     */
+    String[] getParameters();
+
+    /**
+     * Gets this script local variables.
+     * @return the local variables or null
+     * @since 2.1
+     */
+    String[] getLocalVariables();
+
+    /**
+     * 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 
+     * 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
+     * @since 2.1
+     */
+    Set<List<String>> getVariables();
+
+    /**
+     * Creates a Callable from this script.
+     * <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
+     * @return the callable
+     * @since 2.1
+     */
+    Callable<Object> callable(JexlContext context);
+
+    /**
+     * Creates a Callable from this script.
+     * <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
+     * @return the callable
+     * @since 2.1
+     */
+    Callable<Object> callable(JexlContext context, Object... args);
 }

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java?rev=1210927&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java Tue Dec  6 14:19:33 2011
@@ -0,0 +1,313 @@
+/*
+ * 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;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A simple JEXL Template engine.
+ * <p>
+ * At the base is an evaluator similar to the Unified EL evaluator used in JSP/JSF based on JEXL.
+ * At the top is a template engine that uses JEXL (instead of OGNL/VTL) as the scripting
+  * language.
+ * </p>
+ * <p>
+ * The evaluator is intended to be used in configuration modules, XML based frameworks or JSP taglibs
+ * and facilitate the implementation of expression evaluation.
+ * </p>
+ * <p>
+ * The template engine is intended to output text, html, XML.
+ * </p>
+ * @since 2.0
+ */
+public abstract class JxltEngine {
+    /**
+     * The sole type of (runtime) exception the JxltEngine can throw.
+     */
+    public static class Exception extends RuntimeException {
+        /** Serial version UID. */
+        private static final long serialVersionUID = 201112030113L;
+
+        /**
+         * Creates an Exception.
+         * @param msg the exception message
+         * @param cause the exception cause
+         */
+        public Exception(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
+     * A unified expression can mix immediate, deferred and nested sub-expressions as well as string constants;
+     * <ul>
+     * <li>The "immediate" syntax is of the form <code>"...${jexl-expr}..."</code></li>
+     * <li>The "deferred" syntax is of the form <code>"...#{jexl-expr}..."</code></li>
+     * <li>The "nested" syntax is of the form <code>"...#{...${jexl-expr0}...}..."</code></li>
+     * <li>The "composite" syntax is of the form <code>"...${jexl-expr0}... #{jexl-expr1}..."</code></li>
+     * </ul>
+     * <p>
+     * Deferred & immediate expression carry different intentions:
+     * <ul>
+     * <li>An immediate expression indicate that evaluation is intended to be performed close to
+     * the definition/parsing point.</li>
+     * <li>A deferred expression indicate that evaluation is intended to occur at a later stage.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * For instance: <code>"Hello ${name}, now is #{time}"</code> is a composite "deferred" expression since one
+     * of its subexpressions is deferred. Furthermore, this (composite) expression intent is
+     * to perform two evaluations; one close to its definition and another one in a later
+     * phase.
+     * </p>
+     * <p>
+     * The API reflects this feature in 2 methods, prepare and evaluate. The prepare method
+     * will evaluate the immediate subexpression and return an expression that contains only
+     * the deferred subexpressions (& constants), a prepared expression. Such a prepared expression
+     * is suitable for a later phase evaluation that may occur with a different JexlContext.
+     * Note that it is valid to call evaluate without prepare in which case the same JexlContext
+     * is used for the 2 evaluation phases.
+     * </p>
+     * <p>
+     * In the most common use-case where deferred expressions are to be kept around as properties of objects,
+     * one should createExpression & prepare an expression before storing it and evaluate it each time
+     * the property storing it is accessed.
+     * </p>
+     * <p>
+     * Note that nested expression use the JEXL syntax as in:
+     * <code>"#{${bar}+'.charAt(2)'}"</code>
+     * The most common mistake leading to an invalid expression being the following:
+     * <code>"#{${bar}charAt(2)}"</code>
+     * </p>
+     * <p>Also note that methods that createExpression evaluate expressions may throw <em>unchecked</em> exceptions;
+     * The {@link JxltEngine.Exception} are thrown when the engine instance is in "non-silent" mode
+     * but since these are RuntimeException, user-code <em>should</em> catch them where appropriate.
+     * </p>
+     * @since 2.0
+     */
+    public interface UnifiedExpression {
+        /**
+         * Generates this expression's string representation.
+         * @return the string representation
+         */
+        String asString();
+
+        /**
+         * Adds this expression's string representation to a StringBuilder.
+         * @param strb the builder to fill
+         * @return the builder argument
+         */
+        StringBuilder asString(StringBuilder strb);
+
+        /**
+         * Evaluates this expression.
+         * <p>
+         * If the underlying JEXL engine is silent, errors will be logged through its logger as warning.
+         * </p>
+         * @param context the variable context
+         * @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is
+         * running in silent mode
+         * @throws {@link JxltEngine$Exception} if an error occurs and the {@link JexlEngine} is not silent
+         */
+        Object evaluate(JexlContext context);
+
+        /**
+         * Retrieves this expression's source expression.
+         * <p>
+         * If this expression was prepared, this allows to retrieve the
+         * original expression that lead to it.</p>
+         * <p>Other expressions return themselves.</p>
+         * @return the source expression
+         */
+        UnifiedExpression getSource();
+
+        /**
+         * Gets the list of variables accessed by this expression.
+         * <p>This method will visit all nodes of the sub-expressions and extract all variables whether they
+         * are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
+         * @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
+         */
+        Set<List<String>> getVariables();
+
+        /**
+         * Checks whether this expression is deferred.
+         * @return true if deferred, false otherwise
+         */
+        boolean isDeferred();
+
+        /**
+         * Checks whether this expression is immediate.
+         * @return true if immediate, false otherwise
+         */
+        boolean isImmediate();
+
+        /**
+         * Evaluates the immediate sub-expressions.
+         * <p>
+         * When the expression is dependant upon immediate and deferred sub-expressions,
+         * evaluates the immediate sub-expressions with the context passed as parameter
+         * and returns this expression deferred form.
+         * </p>
+         * <p>
+         * In effect, this binds the result of the immediate sub-expressions evaluation in the
+         * context, allowing to differ evaluation of the remaining (deferred) expression within another context.
+         * This only has an effect to nested & composite expressions that contain differed & immediate sub-expressions.
+         * </p>
+         * <p>
+         * If the underlying JEXL engine is silent, errors will be logged through its logger as warning.
+         * </p>
+         * @param context the context to use for immediate expression evaluations
+         * @return an {@link UnifiedExpression} or null if an error occurs and the {@link JexlEngine} is running
+         * in silent mode
+         * @throws {@link JxltEngine.Exception} if an error occurs and the {@link JexlEngine}
+         * is not in silent mode
+         */
+        UnifiedExpression prepare(JexlContext context);
+
+        /**
+         * Formats this expression, adding its source string representation in
+         * comments if available: 'expression /*= source *\/'' .
+         * @return the formatted expression string
+         */
+        @Override
+        String toString();
+    }
+
+    /**
+     * Creates a a {@link UnifiedExpression} from an expression string.
+     * Uses & fills up the expression cache if any.
+     * <p>
+     * If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.
+     * </p>
+     * @param expression the {@link Template} string expression
+     * @return the {@link UnifiedExpression}, null if silent and an error occured
+     * @throws {@link JxltEngine.Exception} if an error occurs and the {@link JexlEngine} is not silent
+     */
+    public abstract UnifiedExpression createExpression(String expression);
+
+    /**
+     * A template is a JEXL script that evaluates by writing its content through a Writer.
+     * <p>
+     * The source text is parsed considering each line beginning with '$$' (as default pattern) as JEXL script code
+     * and all others as Unified JEXL expressions; those expressions will be invoked from the script during
+     * evaluation and their output gathered through a writer. 
+     * It is thus possible to use looping or conditional construct "around" expressions generating output.
+     * </p>
+     * For instance:
+     * <p><blockquote><pre>
+     * $$ for(var x : [1, 3, 5, 42, 169]) {
+     * $$   if (x == 42) {
+     * Life, the universe, and everything
+     * $$   } else if (x > 42) {
+     * The value $(x} is over fourty-two
+     * $$   } else {
+     * The value ${x} is under fourty-two
+     * $$   }
+     * $$ }
+     * </pre></blockquote>
+     * Will evaluate as:
+     * <p><blockquote><pre>
+     * The value 1 is under fourty-two
+     * The value 3 is under fourty-two
+     * The value 5 is under fourty-two
+     * Life, the universe, and everything
+     * The value 169 is over fourty-two
+     * </pre></blockquote>
+     * <p>
+     * During evaluation, the template context exposes its writer as '$jexl' which is safe to use in this case.
+     * This allows writing directly through the writer without adding new-lines as in:
+     * <p><blockquote><pre>
+     * $$ for(var cell : cells) { $jexl.print(cell); $jexl.print(';') }
+     * </pre></blockquote>
+     * </p>
+     * <p>
+     * A template is expanded as one JEXL script and a list of template expressions; each template expression is
+     * being replaced in the script by a call to jexl:print(expr) (the expr is in fact the expr number in the template).
+     * This integration uses a specialized JexlContext (TemplateContext) that serves as a namespace (for jexl:)
+     * and stores the template expression array and the writer (java.io.Writer) that the 'jexl:print(...)'
+     * delegates the output generation to.
+     * </p>
+     */
+    public interface Template {
+        /**
+         * Recreate the template source from its inner components.
+         * @return the template source rewritten
+         */
+        String asString();
+
+        /**
+         * Evaluates this template.
+         * @param context the context to use during evaluation
+         * @param writer the writer to use for output
+         */
+        void evaluate(JexlContext context, Writer writer);
+
+        /**
+         * Evaluates this template.
+         * @param context the context to use during evaluation
+         * @param writer the writer to use for output
+         * @param args the arguments
+         */
+        void evaluate(JexlContext context, Writer writer, Object... args);
+
+        /**
+         * Prepares this template by expanding any contained deferred TemplateExpression.
+         * @param context the context to prepare against
+         * @return the prepared version of the template
+         */
+        Template prepare(JexlContext context);
+    }
+
+    /**
+     * Creates a new template.
+     * @param prefix the directive prefix
+     * @param source the source
+     * @param parms the parameter names
+     * @return the template
+     */
+    public abstract Template createTemplate(String prefix, Reader source, String... parms);
+
+    /**
+     * Creates a new template.
+     * @param source the source
+     * @param parms the parameter names
+     * @return the template
+     */
+    public abstract Template createTemplate(String source, String... parms);
+
+    /**
+     * Creates a new template.
+     * @param source the source
+     * @return the template
+     */
+    public abstract Template createTemplate(String source);
+
+    /**
+     * Gets the {@link JexlEngine} underlying this template engine.
+     * @return the JexlEngine
+     */
+    public abstract JexlEngine getEngine();
+
+    /**
+     * Clears the cache.
+     */
+    public abstract void clearCache();
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Main.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Main.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Main.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Main.java Tue Dec  6 14:19:33 2011
@@ -17,6 +17,7 @@
 
 package org.apache.commons.jexl3;
 
+import org.apache.commons.jexl3.internal.Engine;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.InputStreamReader;
@@ -42,11 +43,11 @@ public class Main {
      * @throws Exception if parsing or IO fail
      */
     public static void main(String[] args) throws Exception {
-        JexlEngine engine = new JexlEngine();
+        JexlEngine engine = new Engine();
         JexlContext context = new MapContext();
         context.set("args", args);
         if (args.length == 1) {
-            Script script = engine.createScript(new File(args[0]));
+            JexlScript script = engine.createScript(new File(args[0]));
             Object value = script.execute(context);
             System.out.println("Return value: " + value);
         } else {
@@ -55,7 +56,7 @@ public class Main {
             System.out.print("> ");
             while (null != (line = console.readLine())) {
                 try {
-                    Expression expression = engine.createExpression(line);
+                    JexlExpression expression = engine.createExpression(line);
                     Object value = expression.evaluate(context);
                     System.out.println("Return value: " + value);
                 } catch (JexlException e) {

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/MapContext.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/MapContext.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/MapContext.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/MapContext.java Tue Dec  6 14:19:33 2011
@@ -45,17 +45,17 @@ public class MapContext implements JexlC
         map = vars == null ? new HashMap<String, Object>() : vars;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public boolean has(String name) {
         return map.containsKey(name);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public Object get(String name) {
         return map.get(name);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void set(String name, Object value) {
         map.put(name, value);
     }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ObjectContext.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ObjectContext.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ObjectContext.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ObjectContext.java Tue Dec  6 14:19:33 2011
@@ -37,17 +37,17 @@ public class ObjectContext<T> implements
         this.object = wrapped;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public Object get(String name) {
         return jexl.getProperty(object, name);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void set(String name, Object value) {
         jexl.setProperty(object, name, value);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public boolean has(String name) {
         return jexl.getUberspect().getPropertyGet(object, name, null) != null;
     }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ReadonlyContext.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ReadonlyContext.java?rev=1210927&r1=1210926&r2=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ReadonlyContext.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/ReadonlyContext.java Tue Dec  6 14:19:33 2011
@@ -16,23 +16,30 @@
  */
 package org.apache.commons.jexl3;
 
+import java.math.MathContext;
+import java.util.Map;
+
 /**
  * A readonly context wrapper.
  * @since 3.0
  */
-public final class ReadonlyContext implements JexlContext {
+public final class ReadonlyContext implements JexlContext, JexlEngine.Options {
     /** The wrapped context. */
     private final JexlContext wrapped;
+    /** The wrapped engine options. */
+    private final JexlEngine.Options options;
 
     /**
      * Creates a new readonly context.
      * @param context the wrapped context
+     * @param eopts the engine evaluation options
      */
-    public ReadonlyContext(JexlContext context) {
+    public ReadonlyContext(JexlContext context, JexlEngine.Options eopts) {
         wrapped = context;
+        options = eopts;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public Object get(String name) {
         return wrapped.get(name);
     }
@@ -42,12 +49,44 @@ public final class ReadonlyContext imple
      * @param name the unused variable name
      * @param value the unused variable value
      */
+    @Override
     public void set(String name, Object value) {
         throw new UnsupportedOperationException("Not supported.");
     }
 
-    /** {@inheritDoc} */
+    @Override
     public boolean has(String name) {
         return wrapped.has(name);
     }
+
+    @Override
+    public Boolean isSilent() {
+        return options == null? null : options.isSilent();
+    }
+
+    @Override
+    public Boolean isStrict() {
+        // egnie
+        return options == null? null : options.isStrict();
+    }
+    
+    @Override
+    public Boolean isStrictArithmetic() {
+        return options == null? null : options.isStrict();
+    }
+
+    @Override
+    public MathContext getArithmeticMathContext() {
+        return options == null? null : options.getArithmeticMathContext();
+    }
+
+    @Override
+    public int getArithmeticMathScale() {
+        return options == null? -1 : options.getArithmeticMathScale();
+    }
+
+    @Override
+    public Map<String, Object> getNamespaces() {
+        return options == null? null : options.getNamespaces();
+    }
 }

Copied: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java (from r1209060, commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Debugger.java)
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java?p2=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java&p1=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Debugger.java&r1=1209060&r2=1210927&rev=1210927&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/Debugger.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java Tue Dec  6 14:19:33 2011
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.jexl3;
+package org.apache.commons.jexl3.internal;
 
 import java.util.regex.Pattern;
+import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.parser.ASTAdditiveNode;
 import org.apache.commons.jexl3.parser.ASTAdditiveOperator;
-import org.apache.commons.jexl3.parser.ASTAmbiguous;
 import org.apache.commons.jexl3.parser.ASTAndNode;
 import org.apache.commons.jexl3.parser.ASTArrayAccess;
 import org.apache.commons.jexl3.parser.ASTArrayLiteral;
@@ -69,7 +69,6 @@ import org.apache.commons.jexl3.parser.A
 import org.apache.commons.jexl3.parser.JexlNode;
 
 import org.apache.commons.jexl3.parser.ParserVisitor;
-import org.apache.commons.jexl3.parser.SimpleNode;
 
 /**
  * Helps pinpoint the cause of problems in expressions that fail during evaluation.
@@ -81,7 +80,7 @@ import org.apache.commons.jexl3.parser.S
  * the error.
  * @since 2.0
  */
-final class Debugger extends ParserVisitor {
+public final class Debugger extends ParserVisitor {
     /** The builder to compose messages. */
     private final StringBuilder builder;
     /** The cause of the issue to debug. */
@@ -94,12 +93,25 @@ final class Debugger extends ParserVisit
     /**
      * Creates a Debugger.
      */
-    Debugger() {
+    public Debugger() {
         builder = new StringBuilder();
         cause = null;
         start = 0;
         end = 0;
     }
+    
+    /**
+     * Position the debugger on the root of a script.
+     * @param jscript the script
+     * @return true if the script was a {@link Script} instance, false otherwise
+     */
+    public boolean debug(JexlScript jscript) {
+        if (jscript instanceof Script) {
+            return debug(((Script) jscript).script);
+        } else {
+            return false;
+        }
+    }
 
     /**
      * Seeks the location of an error cause (a node) in an expression.
@@ -268,7 +280,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTAdditiveNode node, Object data) {
         // need parenthesis if not in operator precedence order
         boolean paren = node.jjtGetParent() instanceof ASTMulNode
@@ -288,7 +300,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTAdditiveOperator node, Object data) {
         builder.append(' ');
         builder.append(node.image);
@@ -296,12 +308,12 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTAndNode node, Object data) {
         return infixChildren(node, " && ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTArrayAccess node, Object data) {
         accept(node.jjtGetChild(0), data);
         int num = node.jjtGetNumChildren();
@@ -313,7 +325,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTArrayLiteral node, Object data) {
         int num = node.jjtGetNumChildren();
         builder.append("[ ");
@@ -328,34 +340,34 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTAssignment node, Object data) {
         return infixChildren(node, " = ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTBitwiseAndNode node, Object data) {
         return infixChildren(node, " & ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTBitwiseComplNode node, Object data) {
         return prefixChild(node, "~", data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTBitwiseOrNode node, Object data) {
         boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
         return infixChildren(node, " | ", paren, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTBitwiseXorNode node, Object data) {
         boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
         return infixChildren(node, " ^ ", paren, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTBlock node, Object data) {
         builder.append("{ ");
         int num = node.jjtGetNumChildren();
@@ -367,12 +379,12 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTDivNode node, Object data) {
         return infixChildren(node, " / ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTEmptyFunction node, Object data) {
         builder.append("empty(");
         accept(node.jjtGetChild(0), data);
@@ -380,22 +392,22 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTEQNode node, Object data) {
         return infixChildren(node, " == ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTERNode node, Object data) {
         return infixChildren(node, " =~ ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTFalseNode node, Object data) {
         return check(node, "false", data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTForeachStatement node, Object data) {
         builder.append("for(");
         accept(node.jjtGetChild(0), data);
@@ -410,12 +422,12 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTGENode node, Object data) {
         return infixChildren(node, " >= ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTGTNode node, Object data) {
         return infixChildren(node, " > ", false, data);
     }
@@ -423,7 +435,7 @@ final class Debugger extends ParserVisit
     /** Checks identifiers that contain space, quote, double-quotes or backspace. */
     private static final Pattern QUOTED_IDENTIFIER = Pattern.compile("['\"\\s\\\\]");
     
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTIdentifier node, Object data) {
         String image = node.image;
         if (QUOTED_IDENTIFIER.matcher(image).find()) {
@@ -433,7 +445,7 @@ final class Debugger extends ParserVisit
         return check(node, image, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTIfStatement node, Object data) {
         builder.append("if (");
         accept(node.jjtGetChild(0), data);
@@ -452,12 +464,12 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTNumberLiteral node, Object data) {
         return check(node, node.image, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTJexlScript node, Object data) {
         int num = node.jjtGetNumChildren();
         for (int i = 0; i < num; ++i) {
@@ -467,17 +479,17 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTLENode node, Object data) {
         return infixChildren(node, " <= ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTLTNode node, Object data) {
         return infixChildren(node, " < ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTMapEntry node, Object data) {
         accept(node.jjtGetChild(0), data);
         builder.append(" : ");
@@ -485,7 +497,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTMapLiteral node, Object data) {
         int num = node.jjtGetNumChildren();
         builder.append("{ ");
@@ -502,7 +514,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTConstructorNode node, Object data) {
         int num = node.jjtGetNumChildren();
         builder.append("new ");
@@ -516,7 +528,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTFunctionNode node, Object data) {
         int num = node.jjtGetNumChildren();
         accept(node.jjtGetChild(0), data);
@@ -533,7 +545,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTMethodNode node, Object data) {
         int num = node.jjtGetNumChildren();
         accept(node.jjtGetChild(0), data);
@@ -548,47 +560,47 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTModNode node, Object data) {
         return infixChildren(node, " % ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTMulNode node, Object data) {
         return infixChildren(node, " * ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTNENode node, Object data) {
         return infixChildren(node, " != ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTNRNode node, Object data) {
         return infixChildren(node, " !~ ", false, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTNotNode node, Object data) {
         builder.append("!");
         accept(node.jjtGetChild(0), data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTNullLiteral node, Object data) {
         check(node, "null", data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTOrNode node, Object data) {
         // need parenthesis if not in operator precedence order
         boolean paren = node.jjtGetParent() instanceof ASTAndNode;
         return infixChildren(node, " || ", paren, data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTReference node, Object data) {
         int num = node.jjtGetNumChildren();
         accept(node.jjtGetChild(0), data);
@@ -599,7 +611,7 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTReferenceExpression node, Object data) {
         JexlNode first = node.jjtGetChild(0);
         builder.append('(');
@@ -614,14 +626,14 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTReturnStatement node, Object data) {
         builder.append("return ");
         accept(node.jjtGetChild(0), data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTSizeFunction node, Object data) {
         builder.append("size(");
         accept(node.jjtGetChild(0), data);
@@ -629,19 +641,19 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTSizeMethod node, Object data) {
         check(node, "size()", data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTStringLiteral node, Object data) {
         String img = node.image.replace("'", "\\'");
         return check(node, "'" + img + "'", data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTTernaryNode node, Object data) {
         accept(node.jjtGetChild(0), data);
         if (node.jjtGetNumChildren() > 2) {
@@ -657,25 +669,25 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTTrueNode node, Object data) {
         check(node, "true", data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTUnaryMinusNode node, Object data) {
         return prefixChild(node, "-", data);
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTVar node, Object data) {
         builder.append("var ");
         check(node, node.image, data);
         return data;
     }
 
-    /** {@inheritDoc} */
+    @Override
     protected Object visit(ASTWhileStatement node, Object data) {
         builder.append("while (");
         accept(node.jjtGetChild(0), data);
@@ -688,13 +700,4 @@ final class Debugger extends ParserVisit
         return data;
     }
 
-    /** {@inheritDoc} */
-    protected Object visit(SimpleNode node, Object data) {
-        throw new UnsupportedOperationException("unexpected type of node");
-    }
-
-    /** {@inheritDoc} */
-    protected Object visit(ASTAmbiguous node, Object data) {
-        throw new UnsupportedOperationException("unexpected type of node");
-    }
 }
\ No newline at end of file

Added: 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=1210927&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java Tue Dec  6 14:19:33 2011
@@ -0,0 +1,1142 @@
+/*
+ * 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 java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.Reader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import org.apache.commons.jexl3.JexlArithmetic;
+import org.apache.commons.jexl3.JexlBuilder;
+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.JexlInfoHandle;
+import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.jexl3.parser.ParseException;
+import org.apache.commons.jexl3.parser.Parser;
+import org.apache.commons.jexl3.parser.JexlNode;
+import org.apache.commons.jexl3.parser.TokenMgrError;
+import org.apache.commons.jexl3.parser.ASTJexlScript;
+import org.apache.commons.jexl3.parser.ASTArrayAccess;
+import org.apache.commons.jexl3.parser.ASTIdentifier;
+import org.apache.commons.jexl3.parser.ASTReference;
+
+import org.apache.commons.jexl3.introspection.JexlUberspect;
+import org.apache.commons.jexl3.introspection.JexlMethod;
+import org.apache.commons.jexl3.internal.introspection.Uberspect;
+
+/**
+ * A JexlEngine implementation.
+ * @since 2.0
+ */
+public class Engine extends JexlEngine {
+    /**
+     * An empty/static/non-mutable JexlContext used instead of null context.
+     */
+    public static final JexlContext EMPTY_CONTEXT = new JexlContext() {
+        @Override
+        public Object get(String name) {
+            return null;
+        }
+
+        @Override
+        public boolean has(String name) {
+            return false;
+        }
+
+        @Override
+        public void set(String name, Object value) {
+            throw new UnsupportedOperationException("Not supported in void context.");
+        }
+    };
+
+    /**
+     *  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
+     * instead of the default one.</p>
+     * <p>Implemented as on demand holder idiom.</p>
+     */
+    private static final class UberspectHolder {
+        /** The default uberspector that handles all introspection patterns. */
+        private static final Uberspect UBERSPECT =
+                new Uberspect(LogFactory.getLog(JexlEngine.class));
+
+        /** Non-instantiable. */
+        private UberspectHolder() {
+        }
+    }
+    /**
+     * The JexlUberspect instance.
+     */
+    protected final JexlUberspect uberspect;
+    /**
+     * The {@link JexlArithmetic} instance.
+     */
+    protected final JexlArithmetic arithmetic;
+    /**
+     * The Log to which all JexlEngine messages will be logged.
+     */
+    protected final Log logger;
+    /**
+     * The {@link Parser}; when parsing expressions, this engine synchronizes on the parser.
+     */
+    protected final Parser parser = new Parser(new StringReader(";")); //$NON-NLS-1$
+    /**
+     * Whether this engine considers unknown variables, methods and constructors as errors.
+     */
+    protected final boolean strict;
+    /**
+     * Whether expressions evaluated by this engine will throw exceptions (false) or 
+     * return null (true) on errors. Default is false.
+     */
+    protected final boolean silent;
+    /**
+     * Whether error messages will carry debugging information.
+     */
+    protected final boolean debug;
+    /**
+     *  The map of 'prefix:function' to object implementing the namespaces.
+     */
+    protected final Map<String, Object> functions;
+    /**
+     * The expression cache.
+     */
+    protected final SoftCache<String, ASTJexlScript> cache;
+    /**
+     * The default cache load factor.
+     */
+    private static final float LOAD_FACTOR = 0.75f;
+
+    /**
+     * Creates an engine with default arguments.
+     */
+    public Engine() {
+        this(new JexlBuilder());
+    }
+
+    /**
+     * Creates a JEXL engine using the provided {@link Uberspect}, (@link JexlArithmetic),
+     * a function map and logger.
+     * @param anUberspect to allow different introspection behaviour
+     * @param anArithmetic to allow different arithmetic behaviour
+     * @param theFunctions an optional map of namespaces (@link setFunctions)
+     * @param log the logger for various messages
+     */
+    public Engine(JexlUberspect anUberspect, JexlArithmetic anArithmetic, Map<String, Object> theFunctions, Log log) {
+        this(new JexlBuilder().uberspect(anUberspect).arithmetic(anArithmetic).namespaces(theFunctions).logger(log));
+    }
+
+    /**
+     * Creates a JEXL engine using the provided {@link JexlBuilder}.
+     * @param conf the builder
+     */
+    public Engine(JexlBuilder conf) {
+        this.uberspect = conf.uberspect() == null ? getUberspect(conf.logger()) : conf.uberspect();
+        this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger();
+        this.functions = conf.namespaces() == null ? Collections.<String, Object>emptyMap() : conf.namespaces();
+        this.silent = conf.silent() == null ? false : conf.silent().booleanValue();
+        this.debug = conf.debug() == null ? true : conf.debug().booleanValue();
+        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());
+    }
+
+    /**
+     *  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
+     * instead of the default one.</p>
+     * @param logger the logger to use for the underlying Uberspect
+     * @return Uberspect the default uberspector instance.
+     */
+    public static Uberspect getUberspect(Log logger) {
+        if (logger == null || logger.equals(LogFactory.getLog(JexlEngine.class))) {
+            return UberspectHolder.UBERSPECT;
+        }
+        return new Uberspect(logger);
+    }
+
+    @Override
+    public JexlUberspect getUberspect() {
+        return uberspect;
+    }
+
+    @Override
+    public JexlArithmetic getArithmetic() {
+        return arithmetic;
+    }
+    
+    @Override
+    public TemplateEngine jxlt() {
+        return new TemplateEngine(this);
+    }
+
+    /**
+     * Checks whether this engine is in debug mode.
+     * @return true if debug is on, false otherwise
+     */
+    @Override
+    public boolean isDebug() {
+        return this.debug;
+    }
+
+    /**
+     * Checks whether this engine throws JexlException during evaluation.
+     * @return true if silent, false (default) otherwise
+     */
+    @Override
+    public boolean isSilent() {
+        return this.silent;
+    }
+
+
+    /**
+     * Checks whether this engine behaves in strict or lenient mode.
+     * Equivalent to !isLenient().
+     * @return true for strict, false for lenient
+     */
+    @Override
+    public final boolean isStrict() {
+        return strict;
+    }
+
+    /**
+     * Sets the class loader used to discover classes in 'new' expressions.
+     * <p>This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.</p>
+     * @param loader the class loader to use
+     */
+    @Override
+    public void setClassLoader(ClassLoader loader) {
+        uberspect.setClassLoader(loader);
+    }
+
+    /**
+     * Retrieves the map of function namespaces.
+     *
+     * @return the map passed in setFunctions or the empty map if the
+     * original was null.
+     */
+    @Override
+    public Map<String, Object> getFunctions() {
+        return functions;
+    }
+
+    /**
+     * An overridable through covariant return JexlExpression creator.
+     * @param text the script text
+     * @param tree the parse AST tree
+     * @return the script instance
+     */
+    protected JexlExpression createExpression(ASTJexlScript tree, String text) {
+        return new Script(this, text, tree);
+    }
+
+    /**
+     * Creates an JexlExpression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An JexlExpression object which can be evaluated with a JexlContext
+     * @throws JexlException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression nor a reference.
+     */
+    @Override
+    public JexlExpression createExpression(String expression) {
+        return createExpression(expression, null);
+    }
+
+    /**
+     * Creates an JexlExpression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An JexlExpression object which can be evaluated with a JexlContext
+     * @param info An info structure to carry debugging information if needed
+     * @throws JexlException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression or a reference.
+     */
+    @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 + "\" ");
+        }
+        return createExpression(tree, expression);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    @Override
+    public Script createScript(String scriptText) {
+        return createScript(scriptText, null, null);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @param names the script parameter names
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    @Override
+    public Script createScript(String scriptText, String... names) {
+        return createScript(scriptText, null, names);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     * It uses an array of parameter names that will be resolved during parsing;
+     * a corresponding array of arguments containing values should be used during evaluation.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @param info An info structure to carry debugging information if needed
+     * @param names the script parameter names
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    @Override
+    public Script createScript(String scriptText, JexlInfo info, String[] names) {
+        if (scriptText == null) {
+            throw new NullPointerException("scriptText is null");
+        }
+        // Parse the expression
+        ASTJexlScript tree = parse(scriptText, info, new Scope(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
+     */
+    protected Script createScript(ASTJexlScript tree, String text) {
+        return new Script(this, text, tree);
+    }
+
+    /**
+     * Creates a Script from a {@link File} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptFile A {@link File} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws IOException if there is a problem reading the script.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    @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() + ")");
+        }
+        BufferedReader reader = new BufferedReader(new FileReader(scriptFile));
+        JexlInfo info = null;
+        if (debug) {
+            info = createInfo(scriptFile.getName(), 0, 0);
+        }
+        return createScript(readerToString(reader), info, null);
+    }
+
+    /**
+     * Creates a Script from a {@link URL} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptUrl A {@link URL} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws IOException if there is a problem reading the script.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    @Override
+    public JexlScript createScript(URL scriptUrl) throws IOException {
+        if (scriptUrl == null) {
+            throw new NullPointerException("scriptUrl 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);
+        }
+        return createScript(readerToString(reader), info, null);
+    }
+
+    /**
+     * Accesses properties of a bean using an expression.
+     * <p>
+     * jexl.get(myobject, "foo.bar"); should equate to
+     * myobject.getFoo().getBar(); (or myobject.getFoo().get("bar"))
+     * </p>
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param bean the bean to get properties from
+     * @param expr the property expression
+     * @return the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    @Override
+    public Object getProperty(Object bean, String expr) {
+        return getProperty(null, bean, expr);
+    }
+
+    /**
+     * Accesses properties of a bean using an expression.
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param context the evaluation context
+     * @param bean the bean to get properties from
+     * @param expr the property expression
+     * @return the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    @Override
+    public Object getProperty(JexlContext context, Object bean, String expr) {
+        if (context == null) {
+            context = EMPTY_CONTEXT;
+        }
+        // synthetize expr using register
+        expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
+        try {
+            parser.ALLOW_REGISTERS = true;
+            Scope scope = new Scope("#0");
+            ASTJexlScript script = parse(expr, null, scope);
+            JexlNode node = script.jjtGetChild(0);
+            Frame frame = script.createFrame(bean);
+            Interpreter interpreter = createInterpreter(context, frame);
+            return node.jjtAccept(interpreter, null);
+        } catch (JexlException xjexl) {
+            if (silent) {
+                logger.warn(xjexl.getMessage(), xjexl.getCause());
+                return null;
+            }
+            throw xjexl;
+        } finally {
+            parser.ALLOW_REGISTERS = false;
+        }
+    }
+
+    /**
+     * Assign properties of a bean using an expression.
+     * <p>
+     * jexl.set(myobject, "foo.bar", 10); should equate to
+     * myobject.getFoo().setBar(10); (or myobject.getFoo().put("bar", 10) )
+     * </p>
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param bean the bean to set properties in
+     * @param expr the property expression
+     * @param value the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    @Override
+    public void setProperty(Object bean, String expr, Object value) {
+        setProperty(null, bean, expr, value);
+    }
+
+    /**
+     * Assign properties of a bean using an expression.
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param context the evaluation context
+     * @param bean the bean to set properties in
+     * @param expr the property expression
+     * @param value the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    @Override
+    public void setProperty(JexlContext context, Object bean, String expr, Object value) {
+        if (context == null) {
+            context = EMPTY_CONTEXT;
+        }
+        // synthetize expr using registers
+        expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + "#1" + ";";
+        try {
+            parser.ALLOW_REGISTERS = true;
+            Scope scope = new Scope("#0", "#1");
+            ASTJexlScript script = parse(expr, null, scope);
+            JexlNode node = script.jjtGetChild(0);
+            Frame frame = script.createFrame(bean, value);
+            Interpreter interpreter = createInterpreter(context, frame);
+            node.jjtAccept(interpreter, null);
+        } catch (JexlException xjexl) {
+            if (silent) {
+                logger.warn(xjexl.getMessage(), xjexl.getCause());
+                return;
+            }
+            throw xjexl;
+        } finally {
+            parser.ALLOW_REGISTERS = false;
+        }
+    }
+
+    /**
+     * Invokes an object's method by name and arguments.
+     * @param obj the method's invoker object
+     * @param meth the method's name
+     * @param args the method's arguments
+     * @return the method returned value or null if it failed and engine is silent
+     * @throws JexlException if method could not be found or failed and engine is not silent
+     */
+    @Override
+    public Object invokeMethod(Object obj, String meth, Object... args) {
+        JexlException xjexl = null;
+        Object result = null;
+        final JexlInfo info = jexlInfo();
+        JexlInfoHandle handle = new JexlInfoHandle() {
+            @Override
+            public JexlInfo jexlInfo() {
+                return info;
+            }
+        };
+        try {
+            JexlMethod method = uberspect.getMethod(obj, meth, args, handle);
+            if (method == null && arithmetic.narrowArguments(args)) {
+                method = uberspect.getMethod(obj, meth, args, handle);
+            }
+            if (method != null) {
+                result = method.invoke(obj, args);
+            } else {
+                xjexl = new JexlException(info, "failed finding method " + meth);
+            }
+        } catch (Exception xany) {
+            xjexl = new JexlException(info, "failed executing method " + meth, xany);
+        } finally {
+            if (xjexl != null) {
+                if (silent) {
+                    logger.warn(xjexl.getMessage(), xjexl.getCause());
+                    return null;
+                }
+                throw xjexl;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Creates a new instance of an object using the most appropriate constructor
+     * based on the arguments.
+     * @param <T> the type of object
+     * @param clazz the class to instantiate
+     * @param args the constructor arguments
+     * @return the created object instance or null on failure when silent
+     */
+    @Override
+    public <T> T newInstance(Class<? extends T> clazz, Object... args) {
+        return clazz.cast(doCreateInstance(clazz, args));
+    }
+
+    /**
+     * Creates a new instance of an object using the most appropriate constructor
+     * based on the arguments.
+     * @param clazz the name of the class to instantiate resolved through this engine's class loader
+     * @param args the constructor arguments
+     * @return the created object instance or null on failure when silent
+     */
+    @Override
+    public Object newInstance(String clazz, Object... args) {
+        return doCreateInstance(clazz, args);
+    }
+
+    /**
+     * 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
+     * @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();
+        JexlInfoHandle handle = new JexlInfoHandle() {
+            @Override
+            public JexlInfo jexlInfo() {
+                return info;
+            }
+        };
+        try {
+            JexlMethod ctor = uberspect.getConstructor(clazz, args, handle);
+            if (ctor == null && arithmetic.narrowArguments(args)) {
+                ctor = uberspect.getConstructor(clazz, args, handle);
+            }
+            if (ctor != null) {
+                result = ctor.invoke(clazz, args);
+            } else {
+                xjexl = new JexlException(info, "failed finding constructor for " + clazz.toString());
+            }
+        } catch (Exception xany) {
+            xjexl = new JexlException(info, "failed executing constructor for " + clazz.toString(), xany);
+        } finally {
+            if (xjexl != null) {
+                if (silent) {
+                    logger.warn(xjexl.getMessage(), xjexl.getCause());
+                    return null;
+                }
+                throw xjexl;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 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, Engine.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;
+            }
+        };
+    }
+
+    /**
+     * Clears the expression cache.
+     */
+    @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
+     */
+    protected Set<List<String>> getVariables(JexlNode script) {
+        Set<List<String>> refs = new LinkedHashSet<List<String>>();
+        getVariables(script, refs, null);
+        return refs;
+    }
+
+    /**
+     * 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
+     */
+    protected void getVariables(JexlNode node, Set<List<String>> refs, List<String> ref) {
+        boolean array = node instanceof ASTArrayAccess;
+        boolean reference = node instanceof ASTReference;
+        int num = node.jjtGetNumChildren();
+        if (array || reference) {
+            List<String> var = ref != null ? ref : new ArrayList<String>();
+            boolean varf = true;
+            for (int i = 0; i < num; ++i) {
+                JexlNode child = node.jjtGetChild(i);
+                if (array) {
+                    if (child instanceof ASTReference && child.jjtGetNumChildren() == 1) {
+                        JexlNode desc = child.jjtGetChild(0);
+                        if (varf && desc.isConstant()) {
+                            String image = desc.image;
+                            if (image == null) {
+                                var.add(new Debugger().data(desc));
+                            } else {
+                                var.add(image);
+                            }
+                        } else if (desc instanceof ASTIdentifier) {
+                            if (((ASTIdentifier) desc).getRegister() < 0) {
+                                List<String> di = new ArrayList<String>(1);
+                                di.add(desc.image);
+                                refs.add(di);
+                            }
+                            var = new ArrayList<String>();
+                            varf = false;
+                        }
+                        continue;
+                    } else if (child instanceof ASTIdentifier) {
+                        if (i == 0 && (((ASTIdentifier) child).getRegister() < 0)) {
+                            var.add(child.image);
+                        }
+                        continue;
+                    }
+                } else {//if (reference) {
+                    if (child instanceof ASTIdentifier) {
+                        if (((ASTIdentifier) child).getRegister() < 0) {
+                            var.add(child.image);
+                        }
+                        continue;
+                    }
+                }
+                getVariables(child, refs, var);
+            }
+            if (!var.isEmpty() && var != ref) {
+                refs.add(var);
+            }
+        } else {
+            for (int i = 0; i < num; ++i) {
+                getVariables(node.jjtGetChild(i), refs, null);
+            }
+        }
+    }
+
+    /**
+     * Gets the array of parameters from a script.
+     * @param script the script
+     * @return the parameters which may be empty (but not null) if no parameters were defined
+     * @since 3.0
+     */
+    protected String[] getParameters(JexlScript script) {
+        return script.getParameters();
+    }
+
+    /**
+     * Gets the array of local variable from a script.
+     * @param script the script
+     * @return the local variables array which may be empty (but not null) if no local variables were defined
+     * @since 3.0
+     */
+    protected String[] getLocalVariables(JexlScript script) {
+        return script.getLocalVariables();
+    }
+
+    /**
+     * A script scope, stores the declaration of parameters and local variables.
+     * @since 3.0
+     */
+    public static final class Scope {
+        /**
+         * The number of parameters.
+         */
+        private final int parms;
+        /**
+         * The map of named registers aka script parameters.
+         * Each parameter is associated to a register and is materialized as an offset in the registers array used
+         * during evaluation.
+         */
+        private Map<String, Integer> namedRegisters = null;
+
+        /**
+         * Creates a new scope with a list of parameters.
+         * @param parameters the list of parameters
+         */
+        public Scope(String... parameters) {
+            if (parameters != null) {
+                parms = parameters.length;
+                namedRegisters = new LinkedHashMap<String, Integer>();
+                for (int p = 0; p < parms; ++p) {
+                    namedRegisters.put(parameters[p], p);
+                }
+            } else {
+                parms = 0;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return namedRegisters == null ? 0 : parms ^ namedRegisters.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof Scope && equals((Scope) o);
+        }
+
+        /**
+         * Whether this frame is equal to another.
+         * @param frame the frame to compare to
+         * @return true if equal, false otherwise
+         */
+        public boolean equals(Scope frame) {
+            if (this == frame) {
+                return true;
+            } else if (frame == null || parms != frame.parms) {
+                return false;
+            } else if (namedRegisters == null) {
+                return frame.namedRegisters == null;
+            } else {
+                return namedRegisters.equals(frame.namedRegisters);
+            }
+        }
+
+        /**
+         * Checks whether an identifier is a local variable or argument, ie stored in a register. 
+         * @param name the register name
+         * @return the register index
+         */
+        public Integer getRegister(String name) {
+            return namedRegisters != null ? namedRegisters.get(name) : null;
+        }
+
+        /**
+         * Declares a local variable.
+         * <p>
+         * This method creates an new entry in the named register map.
+         * </p>
+         * @param name the variable name
+         * @return the register index storing this variable
+         */
+        public Integer declareVariable(String name) {
+            if (namedRegisters == null) {
+                namedRegisters = new LinkedHashMap<String, Integer>();
+            }
+            Integer register = namedRegisters.get(name);
+            if (register == null) {
+                register = Integer.valueOf(namedRegisters.size());
+                namedRegisters.put(name, register);
+            }
+            return register;
+        }
+
+        /**
+         * Creates a frame by copying values up to the number of parameters.
+         * @param values the argument values
+         * @return the arguments array
+         */
+        public Frame createFrame(Object... values) {
+            if (namedRegisters != null) {
+                Object[] arguments = new Object[namedRegisters.size()];
+                if (values != null) {
+                    System.arraycopy(values, 0, arguments, 0, Math.min(parms, values.length));
+                }
+                return new Frame(arguments, namedRegisters.keySet().toArray(new String[0]));
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gets the (maximum) number of arguments this script expects.
+         * @return the number of parameters
+         */
+        public int getArgCount() {
+            return parms;
+        }
+
+        /**
+         * Gets this script registers, i.e. parameters and local variables.
+         * @return the register names
+         */
+        public String[] getRegisters() {
+            return namedRegisters != null ? namedRegisters.keySet().toArray(new String[0]) : new String[0];
+        }
+
+        /**
+         * Gets this script parameters, i.e. registers assigned before creating local variables.
+         * @return the parameter names
+         */
+        public String[] getParameters() {
+            if (namedRegisters != null && parms > 0) {
+                String[] pa = new String[parms];
+                int p = 0;
+                for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
+                    if (entry.getValue().intValue() < parms) {
+                        pa[p++] = entry.getKey();
+                    }
+                }
+                return pa;
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gets this script local variable, i.e. registers assigned to local variables.
+         * @return the parameter names
+         */
+        public String[] getLocalVariables() {
+            if (namedRegisters != null && parms > 0) {
+                String[] pa = new String[parms];
+                int p = 0;
+                for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
+                    if (entry.getValue().intValue() >= parms) {
+                        pa[p++] = entry.getKey();
+                    }
+                }
+                return pa;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * A call frame, created from a scope, stores the arguments and local variables as "registers".
+     * @since 3.0
+     */
+    public static final class Frame {
+        /** Registers or arguments. */
+        private final Object[] registers;
+        /** Parameter and argument names if any. */
+        private final String[] parameters;
+
+        /**
+         * Creates a new frame.
+         * @param r the registers
+         * @param p the parameters
+         */
+        Frame(Object[] r, String[] p) {
+            registers = r;
+            parameters = p;
+        }
+
+        /**
+         * @return the registers
+         */
+        public Object[] getRegisters() {
+            return registers;
+        }
+
+        /**
+         * @return the parameters
+         */
+        public String[] getParameters() {
+            return parameters;
+        }
+    }
+
+    /**
+     * Parses an expression.
+     * @param expression the expression to parse
+     * @param info debug information structure
+     * @param frame the script frame to use
+     * @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;
+        synchronized (parser) {
+            if (cache != null) {
+                script = cache.get(expr);
+                if (script != null) {
+                    Scope f = script.getScope();
+                    if ((f == null && frame == null) || (f != null && f.equals(frame))) {
+                        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;
+                }
+                parser.setFrame(frame);
+                script = parser.parse(reader, jexlInfo);
+                // reaccess in case local variables have been declared
+                frame = parser.getFrame();
+                if (frame != null) {
+                    script.setScope(frame);
+                }
+                if (cache != null) {
+                    cache.put(expr, script);
+                }
+            } catch (TokenMgrError xtme) {
+                throw new JexlException.Tokenization(jexlInfo, expression, xtme);
+            } catch (ParseException xparse) {
+                throw new JexlException.Parsing(jexlInfo, expression, xparse);
+            } finally {
+                parser.setFrame(null);
+            }
+        }
+        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 = null) {
+                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;
+                    }
+                }
+            }
+            if (se != null) {
+                info = createInfo(se.getClassName() + "." + se.getMethodName(), se.getLineNumber(), 0);
+            }
+        }
+        return info;
+    }
+
+}



Mime
View raw message