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 2017/11/29 16:27:05 UTC

svn commit: r1816640 [1/2] - 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/parser/ src/site/xdoc/ src/test/java/org/apache/commons/jexl3...

Author: henrib
Date: Wed Nov 29 16:27:04 2017
New Revision: 1816640

URL: http://svn.apache.org/viewvc?rev=1816640&view=rev
Log:
JEXL-243:
Features, refined version. Allow fine grain on what is syntactically available for both scripts and expressions (and make expressions a subset of script features).

Added:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Source.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java   (with props)
Modified:
    commons/proper/jexl/trunk/RELEASE-NOTES.txt
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlEngine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
    commons/proper/jexl/trunk/src/site/xdoc/changes.xml
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ContextNamespaceTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest200.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/internal/Util.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/parser/ParserTest.java

Modified: commons/proper/jexl/trunk/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/RELEASE-NOTES.txt?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/RELEASE-NOTES.txt (original)
+++ commons/proper/jexl/trunk/RELEASE-NOTES.txt Wed Nov 29 16:27:04 2017
@@ -27,11 +27,15 @@ Version 3.2 is a minor release.
 
 New Features in 3.2:
 ====================
+
+* JEXL-243:      Allow restricting available features in Script/Expressions
 * JEXL-238:      Restrict getLiteralClass to a Number for NumberLiterals
+* JEXL-237:      Ability to restrict usage of certain names when declaring local variables
 * JEXL-236:      Support CharSequence in size(), empty() and contains() operators
 * JEXL-234:      Extend application of operators startsWith and endsWith from String to CharSequence types
 * JEXL-226:      add ?? operator support
 * JEXL-224:      The ability to overload call() operator in customized JexlArithmetic implementation
+* JEXL-212:      Restrict usage of assignment statements in JexlExpression
 
 Bugs Fixed in 3.2:
 ==================

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlBuilder.java?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlBuilder.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlBuilder.java Wed Nov 29 16:27:04 2017
@@ -175,7 +175,9 @@ public class JexlBuilder {
     }
 
     /**
-     * Sets the features the engine will use.
+     * Sets the features the engine will use as a base by default.
+     * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts.
+     * <p>Note also that these will apply to template expressions and scripts.
      * @param f the features
      * @return this builder
      */

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlEngine.java?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlEngine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlEngine.java Wed Nov 29 16:27:04 2017
@@ -322,9 +322,21 @@ public abstract class JexlEngine {
     public abstract JexlExpression createExpression(JexlInfo info, String expression);
 
     /**
+     * Creates a 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 {@link JexlExpression} which can be evaluated using a {@link JexlContext}
+     * @throws JexlException if there is a problem parsing the script
+     */
+    public final JexlExpression createExpression(String expression) {
+        return createExpression(null, expression);
+    }
+    /**
      * Creates a JexlScript from a String containing valid JEXL syntax.
      * This method parses the script and validates the syntax.
      *
+     * @param features A set of features that will be enforced during parsing
      * @param info   An info structure to carry debugging information if needed
      * @param source A string containing valid JEXL syntax
      * @param names  The script parameter names used during parsing; a corresponding array of arguments containing
@@ -332,18 +344,21 @@ public abstract class JexlEngine {
      * @return A {@link JexlScript} which can be executed using a {@link JexlContext}
      * @throws JexlException if there is a problem parsing the script
      */
-    public abstract JexlScript createScript(JexlInfo info, String source, String[] names);
+    public abstract JexlScript createScript(JexlFeatures features, JexlInfo info, String source, String[] names);
 
     /**
-     * Creates a JexlExpression from a String containing valid JEXL syntax.
-     * This method parses the expression which must contain either a reference or an expression.
+     * Creates a JexlScript from a String containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
      *
-     * @param expression A String containing valid JEXL syntax
-     * @return An {@link JexlExpression} which can be evaluated using a {@link JexlContext}
+     * @param info   An info structure to carry debugging information if needed
+     * @param source A string containing valid JEXL syntax
+     * @param names  The script parameter names used during parsing; a corresponding array of arguments containing
+     * values should be used during evaluation
+     * @return A {@link JexlScript} which can be executed using a {@link JexlContext}
      * @throws JexlException if there is a problem parsing the script
      */
-    public final JexlExpression createExpression(String expression) {
-        return createExpression(null, expression);
+    public final JexlScript createScript(JexlInfo info, String source, String[] names) {
+        return createScript(null, null, source, names);
     }
 
     /**
@@ -355,7 +370,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem parsing the script.
      */
     public final JexlScript createScript(String scriptText) {
-        return createScript(null, scriptText, null);
+        return createScript(null, null, scriptText, null);
     }
 
     /**
@@ -369,7 +384,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem parsing the script
      */
     public final JexlScript createScript(String scriptText, String... names) {
-        return createScript(null, scriptText, names);
+        return createScript(null, null, scriptText, names);
     }
 
     /**
@@ -381,7 +396,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the script.
      */
     public final JexlScript createScript(File scriptFile) {
-        return createScript(null, readSource(scriptFile), null);
+        return createScript(null, null, readSource(scriptFile), null);
     }
 
     /**
@@ -395,7 +410,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the script.
      */
     public final JexlScript createScript(File scriptFile, String... names) {
-        return createScript(null, readSource(scriptFile), names);
+        return createScript(null, null, readSource(scriptFile), names);
     }
 
     /**
@@ -410,7 +425,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the script.
      */
     public final JexlScript createScript(JexlInfo info, File scriptFile, String[] names) {
-        return createScript(info, readSource(scriptFile), names);
+        return createScript(null, info, readSource(scriptFile), names);
     }
 
     /**
@@ -436,7 +451,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the script.
      */
     public final JexlScript createScript(URL scriptUrl, String[] names) {
-        return createScript(null, readSource(scriptUrl), names);
+        return createScript(null, null, readSource(scriptUrl), names);
     }
 
     /**
@@ -451,7 +466,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the script.
      */
     public final JexlScript createScript(JexlInfo info, URL scriptUrl, String[] names) {
-        return createScript(info, readSource(scriptUrl), names);
+        return createScript(null, info, readSource(scriptUrl), names);
     }
 
     /**

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlFeatures.java?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlFeatures.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlFeatures.java Wed Nov 29 16:27:04 2017
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.commons.jexl3;
 
 import java.util.Collection;
@@ -24,15 +23,23 @@ import java.util.TreeSet;
 
 /**
  * A set of language feature options.
- *
+ * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
+ * subclass of JexlException.Parsing) when disabled.
+ * <p>
  * <ul>
+ * <li>Registers: register syntax (#number), used internally for {g,s}etProperty
  * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
- * <li>Registers: boolean property allowing parsing of register syntax (#number)
- * <li>Global Side Effect : boolean property controlling assigning/modifying values on global variables
- * <li>Side Effect: boolean property controlling side effects assigning/modifying values on any variable
- * <li>New Instance: boolean property controlling creating new instances through new(...) or using class as functor
- * <li>Loops: boolean property controlling usage of loop constructs (while(true), for(...))
- * <li>Lambda: boolean property controlling usage of script function declarations
+ * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
+ * <li>Side Effect : assigning/modifying values on any variables or left-value
+ * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
+ * <li>New Instance: creating an instance using new(...)
+ * <li>Loops: loop constructs (while(true), for(...))
+ * <li>Lambda: function definitions (()->{...}, function(...) ).
+ * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
+ * - including namespace prefixes - available
+ * <li>Structured literals: arrays, lists, maps, sets, ranges
+ * <li>Pragmas: #pragma x y
+ * <li>Annotation: @annotation statement;
  * </ul>
  * @since 3.2
  */
@@ -44,36 +51,54 @@ public final class JexlFeatures {
     /** Te feature names (for toString()). */
     private static final String[] F_NAMES = {
         "register", "reserved variable", "local variable", "assign/modify",
-        "global assign/modify", "new(...)", "for(...)/while(...)", "function"
+        "global assign/modify", "array reference", "create instance", "loop", "function",
+        "method call", "set/map/array literal", "pragma", "annotation", "script"
     };
     /** Registers feature ordinal. */
-    private static final int REGISTERS = 0;
+    private static final int REGISTER = 0;
     /** Reserved name feature ordinal. */
     public static final int RESERVED = 1;
     /** Locals feature ordinal. */
-    public static final int LOCALS = 2;
+    public static final int LOCAL_VAR = 2;
     /** Side-effects feature ordinal. */
-    public static final int SIDE_EFFECTS = 3;
+    public static final int SIDE_EFFECT = 3;
     /** Global side-effects feature ordinal. */
-    public static final int SIDE_EFFECTS_GLOBALS = 4;
+    public static final int SIDE_EFFECT_GLOBAL = 4;
+    /** Array get is allowed on expr. */
+    public static final int ARRAY_REF_EXPR = 5;
     /** New-instance feature ordinal. */
-    public static final int NEW_INSTANCE = 5;
+    public static final int NEW_INSTANCE = 6;
     /** Loops feature ordinal. */
-    public static final int LOOPS = 6;
+    public static final int LOOP = 7;
+    /** Lambda feature ordinal. */
+    public static final int LAMBDA = 8;
     /** Lambda feature ordinal. */
-    public static final int LAMBDA = 7;
+    public static final int METHOD_CALL = 9;
+    /** Structured literal feature ordinal. */
+    public static final int STRUCTURED_LITERAL = 10;
+    /** Pragma feature ordinal. */
+    public static final int PRAGMA = 11;
+    /** Annotation feature ordinal. */
+    public static final int ANNOTATION = 12;
+    /** Script feature ordinal. */
+    public static final int SCRIPT = 13;
 
     /**
      * Creates an all-features-enabled instance.
      */
     public JexlFeatures() {
-        flags = (1L) // << REGISTERS)
-                | (1L << LOCALS)
-                | (1L << SIDE_EFFECTS)
-                | (1L << SIDE_EFFECTS_GLOBALS)
+        flags = (1L << LOCAL_VAR)
+                | (1L << SIDE_EFFECT)
+                | (1L << SIDE_EFFECT_GLOBAL)
+                | (1L << ARRAY_REF_EXPR)
                 | (1L << NEW_INSTANCE)
-                | (1L << LOOPS)
-                | (1L << LAMBDA);
+                | (1L << LOOP)
+                | (1L << LAMBDA)
+                | (1L << METHOD_CALL)
+                | (1L << STRUCTURED_LITERAL)
+                | (1L << PRAGMA)
+                | (1L << ANNOTATION)
+                | (1L << SCRIPT);
         reservedNames = Collections.emptySet();
     }
 
@@ -86,13 +111,43 @@ public final class JexlFeatures {
         this.reservedNames = features.reservedNames;
     }
 
+    @Override
+    public int hashCode() { //CSOFF: MagicNumber
+        int hash = 3;
+        hash = 53 * hash + (int) (this.flags ^ (this.flags >>> 32));
+        hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final JexlFeatures other = (JexlFeatures) obj;
+        if (this.flags != other.flags) {
+            return false;
+        }
+        if (this.reservedNames != other.reservedNames
+                && (this.reservedNames == null || !this.reservedNames.equals(other.reservedNames))) {
+            return false;
+        }
+        return true;
+    }
+
     /**
      * The text corresponding to a feature code.
      * @param feature the feature number
      * @return the feature name
      */
     public static String stringify(int feature) {
-        return feature >= 0 && feature < F_NAMES.length? F_NAMES[feature] : "unsupported feature";
+        return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
     }
 
     /**
@@ -123,13 +178,13 @@ public final class JexlFeatures {
      * @return true if reserved, false otherwise
      */
     public boolean isReservedName(String name) {
-        return reservedNames.contains(name);
+        return name != null && reservedNames.contains(name);
     }
 
     /**
      * Sets a feature flag.
      * @param feature the feature ordinal
-     * @param flag turn-on, turn off
+     * @param flag    turn-on, turn off
      */
     private void setFeature(int feature, boolean flag) {
         if (flag) {
@@ -149,83 +204,150 @@ public final class JexlFeatures {
     }
 
     /**
-     * Sets whether registers are enabled.
-     * <p>When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
+     * Sets whether register are enabled.
+     * <p>
+     * This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
+     * <p>
+     * When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
      */
-    public JexlFeatures registers(boolean flag) {
-        setFeature(REGISTERS, flag);
+    public JexlFeatures register(boolean flag) {
+        setFeature(REGISTER, flag);
         return this;
     }
 
     /**
      * @return true if register syntax is enabled
      */
-    public boolean supportsRegisters() {
-        return getFeature(REGISTERS);
+    public boolean supportsRegister() {
+        return getFeature(REGISTER);
     }
 
     /**
      * Sets whether local variables are enabled.
-     * <p>When disabled, parsing a script/expression using a local variable or parameter syntax
+     * <p>
+     * When disabled, parsing a script/expression using a local variable or parameter syntax
      * will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
      */
-    public JexlFeatures locals(boolean flag) {
-        setFeature(LOCALS, flag);
+    public JexlFeatures localVar(boolean flag) {
+        setFeature(LOCAL_VAR, flag);
         return this;
     }
 
     /**
      * @return true if local variables syntax is enabled
      */
-    public boolean supportsLocals() {
-        return getFeature(LOCALS);
+    public boolean supportsLocalVar() {
+        return getFeature(LOCAL_VAR);
     }
 
-   /**
+    /**
      * Sets whether side effect expressions on global variables (aka non local) are enabled.
-     * <p>When disabled, parsing a script/expression using syntactical constructs modifying variables
+     * <p>
+     * When disabled, parsing a script/expression using syntactical constructs modifying variables
      * <em>including all potentially ant-ish variables</em> will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
      */
-    public JexlFeatures sideEffectsGlobal(boolean flag) {
-        setFeature(SIDE_EFFECTS_GLOBALS, flag);
+    public JexlFeatures sideEffectGlobal(boolean flag) {
+        setFeature(SIDE_EFFECT_GLOBAL, flag);
         return this;
     }
 
     /**
      * @return true if global variables can be assigned
      */
-    public boolean supportsSideEffectsGlobal() {
-        return getFeature(SIDE_EFFECTS_GLOBALS);
+    public boolean supportsSideEffectGlobal() {
+        return getFeature(SIDE_EFFECT_GLOBAL);
     }
 
-   /**
+    /**
      * Sets whether side effect expressions are enabled.
-     * <p>When disabled, parsing a script/expression using syntactical constructs modifying variables
+     * <p>
+     * When disabled, parsing a script/expression using syntactical constructs modifying variables
      * or members will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
      */
-    public JexlFeatures sideEffects(boolean flag) {
-        setFeature(SIDE_EFFECTS, flag);
+    public JexlFeatures sideEffect(boolean flag) {
+        setFeature(SIDE_EFFECT, flag);
         return this;
     }
 
     /**
      * @return true if side effects are enabled, false otherwise
      */
-    public boolean supportsSideEffects() {
-        return getFeature(SIDE_EFFECTS);
+    public boolean supportsSideEffect() {
+        return getFeature(SIDE_EFFECT);
+    }
+
+    /**
+     * Sets whether array references expressions are enabled.
+     * <p>
+     * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
+     * will throw a parsing exception;
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures arrayReferenceExpr(boolean flag) {
+        setFeature(ARRAY_REF_EXPR, flag);
+        return this;
+    }
+
+    /**
+     * @return true if array references can contain method call expressions, false otherwise
+     */
+    public boolean supportsArrayReferenceExpr() {
+        return getFeature(ARRAY_REF_EXPR);
+    }
+
+    /**
+     * Sets whether method calls expressions are enabled.
+     * <p>
+     * When disabled, parsing a script/expression using 'obj.method()'
+     * will throw a parsing exception;
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures methodCall(boolean flag) {
+        setFeature(METHOD_CALL, flag);
+        return this;
+    }
+
+    /**
+     * @return true if array references can contain expressions, false otherwise
+     */
+    public boolean supportsMethodCall() {
+        return getFeature(METHOD_CALL);
+    }
+
+    /**
+     * Sets whether array/map/set literal expressions are enabled.
+     * <p>
+     * When disabled, parsing a script/expression creating one of these literals
+     * will throw a parsing exception;
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures structuredLiteral(boolean flag) {
+        setFeature(STRUCTURED_LITERAL, flag);
+        return this;
+    }
+
+    /**
+     * @return true if array/map/set literal expressions are supported, false otherwise
+     */
+    public boolean supportsStructuredLiteral() {
+        return getFeature(STRUCTURED_LITERAL);
     }
 
-   /**
+    /**
      * Sets whether creating new instances is enabled.
-     * <p>When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
+     * <p>
+     * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
      * using a class as functor will fail at runtime.
      * @param flag true to enable, false to disable
      * @return this features instance
@@ -244,13 +366,14 @@ public final class JexlFeatures {
 
     /**
      * Sets whether looping constructs are enabled.
-     * <p>When disabled, parsing a script/expression using syntactic looping constructs (for,while)
+     * <p>
+     * When disabled, parsing a script/expression using syntactic looping constructs (for,while)
      * will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
      */
     public JexlFeatures loops(boolean flag) {
-        setFeature(LOOPS, flag);
+        setFeature(LOOP, flag);
         return this;
     }
 
@@ -258,12 +381,13 @@ public final class JexlFeatures {
      * @return true if loops are enabled, false otherwise
      */
     public boolean supportsLoops() {
-        return getFeature(LOOPS);
+        return getFeature(LOOP);
     }
 
-       /**
+    /**
      * Sets whether lambda/function constructs are enabled.
-     * <p>When disabled, parsing a script/expression using syntactic lambda constructs (->,function)
+     * <p>
+     * When disabled, parsing a script/expression using syntactic lambda constructs (->,function)
      * will throw a parsing exception.
      * @param flag true to enable, false to disable
      * @return this features instance
@@ -280,4 +404,72 @@ public final class JexlFeatures {
         return getFeature(LAMBDA);
     }
 
+    /**
+     * Sets whether pragma constructs are enabled.
+     * <p>
+     * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
+     * will throw a parsing exception.
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures pragma(boolean flag) {
+        setFeature(PRAGMA, flag);
+        return this;
+    }
+
+    /**
+     * @return true if pragma are enabled, false otherwise
+     */
+    public boolean supportsPragma() {
+        return getFeature(PRAGMA);
+    }
+
+    /**
+     * Sets whether annotation constructs are enabled.
+     * <p>
+     * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
+     * will throw a parsing exception.
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures annotation(boolean flag) {
+        setFeature(ANNOTATION, flag);
+        return this;
+    }
+
+    /**
+     * @return true if annotation are enabled, false otherwise
+     */
+    public boolean supportsAnnotation() {
+        return getFeature(ANNOTATION);
+    }
+
+    /**
+     * Sets whether scripts constructs are enabled.
+     * <p>
+     * When disabled, parsing a script using syntactic script constructs (statements, ...)
+     * will throw a parsing exception.
+     * @param flag true to enable, false to disable
+     * @return this features instance
+     */
+    public JexlFeatures script(boolean flag) {
+        setFeature(SCRIPT, flag);
+        return this;
+    }
+
+    /**
+     * @return true if scripts are enabled, false otherwise
+     */
+    public boolean supportsScript() {
+        return getFeature(SCRIPT);
+    }
+
+    /**
+     *
+     * @return true if expressions (aka not scripts) are enabled, false otherwise
+     */
+    public boolean supportsExpression() {
+        return !getFeature(SCRIPT);
+    }
+
 }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java Wed Nov 29 16:27:04 2017
@@ -280,7 +280,8 @@ public class Debugger extends ParserVisi
             || child instanceof ASTBlock
             || child instanceof ASTIfStatement
             || child instanceof ASTForeachStatement
-            || child instanceof ASTWhileStatement)) {
+            || child instanceof ASTWhileStatement
+            || child instanceof ASTAnnotation)) {
             builder.append(';');
             if (indent > 0) {
                 builder.append('\n');
@@ -622,15 +623,23 @@ public class Debugger extends ParserVisi
 
     @Override
     protected Object visit(ASTIfStatement node, Object data) {
+        final int numChildren = node.jjtGetNumChildren();
+        // if (...) ...
         builder.append("if (");
         accept(node.jjtGetChild(0), data);
         builder.append(") ");
-        if (node.jjtGetNumChildren() > 1) {
-            acceptStatement(node.jjtGetChild(1), data);
-            if (node.jjtGetNumChildren() > 2) {
-                builder.append(" else ");
-                acceptStatement(node.jjtGetChild(2), data);
-            }
+        acceptStatement(node.jjtGetChild(1), data);
+        //.. else if (...) ...
+        for(int c = 2; c <  numChildren - 1; c += 2) {
+            builder.append(" else if (");
+            accept(node.jjtGetChild(c), data);
+            builder.append(") ");
+            acceptStatement(node.jjtGetChild(c + 1), data);
+        }
+        // else...
+        if (numChildren % 2 == 1) {
+            builder.append(" else ");
+            acceptStatement(node.jjtGetChild(numChildren - 1), data);
         } else {
             builder.append(';');
         }
@@ -993,14 +1002,7 @@ public class Debugger extends ParserVisi
         builder.append('@');
         builder.append(node.getName());
         if (num > 0) {
-            builder.append("(");
-            accept(node.jjtGetChild(0), data);
-            for(int i = 0; i < num; ++i) {
-                builder.append(", ");
-                JexlNode child = node.jjtGetChild(i);
-                acceptStatement(child, data);
-            }
-            builder.append(")");
+            accept(node.jjtGetChild(0), data); // zut
         }
         return null;
     }
@@ -1009,6 +1011,9 @@ public class Debugger extends ParserVisi
     protected Object visit(ASTAnnotatedStatement node, Object data) {
         int num = node.jjtGetNumChildren();
         for (int i = 0; i < num; ++i) {
+            if (i > 0) {// && child instanceof ASTBlock) {
+                builder.append(' ');
+            }
             JexlNode child = node.jjtGetChild(i);
             acceptStatement(child, data);
         }

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=1816640&r1=1816639&r2=1816640&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 Wed Nov 29 16:27:04 2017
@@ -21,6 +21,7 @@ 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.JexlFeatures;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.internal.introspection.SandboxUberspect;
@@ -73,6 +74,10 @@ public class Engine extends JexlEngine {
         private UberspectHolder() {}
     }
     /**
+     * The Log to which all JexlEngine messages will be logged.
+     */
+    protected final Log logger;
+    /**
      * The JexlUberspect instance.
      */
     protected final JexlUberspect uberspect;
@@ -81,18 +86,9 @@ public class Engine extends JexlEngine {
      */
     protected final JexlArithmetic arithmetic;
     /**
-     * The Log to which all JexlEngine messages will be logged.
-     */
-    protected final Log logger;
-    /**
-     * The atomic parsing flag; true whilst parsing.
-     */
-    protected final AtomicBoolean parsing = new AtomicBoolean(false);
-    /**
-     * The {@link Parser}; when parsing expressions, this engine uses the parser if it
-     * is not already in use otherwise it will create a new temporary one.
+     * The map of 'prefix:function' to object implementing the namespaces.
      */
-    protected final Parser parser = new Parser(new StringReader(";")); //$NON-NLS-1$
+    protected final Map<String, Object> functions;
     /**
      * Whether this engine considers unknown variables, methods and constructors as errors.
      */
@@ -113,21 +109,34 @@ public class Engine extends JexlEngine {
      */
     protected final boolean debug;
     /**
-     * The map of 'prefix:function' to object implementing the namespaces.
+     * The atomic parsing flag; true whilst parsing.
      */
-    protected final Map<String, Object> functions;
+    protected final AtomicBoolean parsing = new AtomicBoolean(false);
     /**
-     * The expression cache.
+     * The default charset.
+     */
+    protected final Charset charset;
+    /**
+     * The set of default script parsing features.
      */
-    protected final SoftCache<String, ASTJexlScript> cache;
+    protected final JexlFeatures scriptFeatures;
+    /**
+     * The set of default expression parsing features.
+     */
+    protected final JexlFeatures expressionFeatures;
+    /**
+     * The {@link Parser}; when parsing expressions, this engine uses the parser if it
+     * is not already in use otherwise it will create a new temporary one.
+     */
+    protected final Parser parser = new Parser(new StringReader(";")); //$NON-NLS-1$
     /**
      * The expression max length to hit the cache.
      */
     protected final int cacheThreshold;
     /**
-     * The default charset.
+     * The expression cache.
      */
-    protected final Charset charset;
+    protected final SoftCache<Source, ASTJexlScript> cache;
     /**
      * The default jxlt engine.
      */
@@ -145,28 +154,34 @@ public class Engine extends JexlEngine {
      * @param conf the builder
      */
     public Engine(JexlBuilder conf) {
-        JexlSandbox sandbox = conf.sandbox();
+        // options:
+        this.strict = conf.strict() == null ? true : conf.strict();
+        this.silent = conf.silent() == null ? false : conf.silent();
+        this.cancellable = conf.cancellable() == null ? !silent && strict : conf.cancellable();
+        this.debug = conf.debug() == null ? true : conf.debug();
+        // core properties:
         JexlUberspect uber = conf.uberspect() == null ? getUberspect(conf.logger(), conf.strategy()) : conf.uberspect();
         ClassLoader loader = conf.loader();
         if (loader != null) {
             uber.setClassLoader(loader);
         }
+        JexlSandbox sandbox = conf.sandbox();
         if (sandbox == null) {
             this.uberspect = uber;
         } else {
             this.uberspect = new SandboxUberspect(uber, sandbox);
         }
-        parser.setFeatures(conf.features() == null? JexlEngine.DEFAULT_FEATURES : conf.features());
         this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger();
-        this.functions = conf.namespaces() == null ? Collections.<String, Object>emptyMap() : conf.namespaces();
-        this.strict = conf.strict() == null ? true : conf.strict();
-        this.silent = conf.silent() == null ? false : conf.silent();
-        this.cancellable = conf.cancellable() == null ? !silent && strict : conf.cancellable();
-        this.debug = conf.debug() == null ? true : conf.debug();
         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();
+        this.functions = conf.namespaces() == null ? Collections.<String, Object>emptyMap() : conf.namespaces();
+        // parsing & features:
+        JexlFeatures features = conf.features() == null? DEFAULT_FEATURES : conf.features();
+        this.expressionFeatures = new JexlFeatures(features).script(false);
+        this.scriptFeatures = new JexlFeatures(features).script(true);
         this.charset = conf.charset();
+        // caching:
+        this.cache = conf.cache() <= 0 ? null : new SoftCache<Source, ASTJexlScript>(conf.cache());
+        this.cacheThreshold = conf.cacheThreshold();
         if (uberspect == null) {
             throw new IllegalArgumentException("uberspect can not be null");
         }
@@ -252,26 +267,34 @@ public class Engine extends JexlEngine {
         return new Interpreter(this, context, frame);
     }
 
+
     @Override
-    public Script createScript(JexlInfo info, String scriptText, String[] names) {
+    public Script createExpression(JexlInfo info, String expression) {
+        return createScript(expressionFeatures, info, expression, null);
+    }
+
+    @Override
+    public Script createScript(JexlFeatures features, JexlInfo info, String scriptText, String[] names) {
         if (scriptText == null) {
             throw new NullPointerException("source is null");
         }
         String source = trimSource(scriptText);
         Scope scope = names == null ? null : new Scope(null, names);
-        ASTJexlScript tree = parse(info, source, scope, false, false);
+        ASTJexlScript tree = parse(info, features == null? scriptFeatures : features, source, scope);
         return new Script(this, source, tree);
     }
 
-    @Override
-    public Script createExpression(JexlInfo info, String expression) {
-        if (expression == null) {
-            throw new NullPointerException("source is null");
-        }
-        String source = trimSource(expression);
-        ASTJexlScript tree = parse(info, source, null, false, true);
-        return new Script(this, source, tree);
-    }
+    /**
+     * The features allowed for property set/get methods.
+     */
+    protected static final JexlFeatures PROPERTY_FEATURES = new JexlFeatures()
+            .localVar(false)
+            .loops(false)
+            .lambda(false)
+            .script(false)
+            .arrayReferenceExpr(false)
+            .methodCall(false)
+            .register(true);
 
     @Override
     public Object getProperty(Object bean, String expr) {
@@ -288,7 +311,7 @@ public class Engine extends JexlEngine {
         src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src;
         try {
             final Scope scope = new Scope(null, "#0");
-            final ASTJexlScript script = parse(null, src, scope, true, true);
+            final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
             final JexlNode node = script.jjtGetChild(0);
             final Scope.Frame frame = script.createFrame(bean);
             final Interpreter interpreter = createInterpreter(context, frame);
@@ -312,12 +335,12 @@ public class Engine extends JexlEngine {
         if (context == null) {
             context = EMPTY_CONTEXT;
         }
-        // synthetize expr using registers
+        // synthetize expr using register
         String src = trimSource(expr);
         src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=" + "#1";
         try {
             final Scope scope = new Scope(null, "#0", "#1");
-            final ASTJexlScript script = parse(null, src, scope, true, true);
+            final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
             final JexlNode node = script.jjtGetChild(0);
             final Scope.Frame frame = script.createFrame(bean, value);
             final Interpreter interpreter = createInterpreter(context, frame);
@@ -579,18 +602,33 @@ public class Engine extends JexlEngine {
      * Parses an expression.
      *
      * @param info      information structure
+     * @param expr     whether we parse an expression or a feature
+     * @param src      the expression to parse
+     * @param scope     the script frame
+     * @return the parsed tree
+     * @throws JexlException if any error occurred during parsing
+     */
+    protected ASTJexlScript parse(JexlInfo info, boolean expr, String src, Scope scope) {
+        return parse(info, expr? this.expressionFeatures : this.scriptFeatures, src, scope);
+    }
+
+    /**
+     * Parses an expression.
+     *
+     * @param info      information structure
+     * @param parsingf  the set of parsing features
      * @param src      the expression to parse
      * @param scope     the script frame
-     * @param registers whether the parser should allow the unnamed '#number' syntax for 'registers'
-     * @param expression whether the parser allows scripts or only expressions
      * @return the parsed tree
      * @throws JexlException if any error occurred during parsing
      */
-    protected ASTJexlScript parse(JexlInfo info, String src, Scope scope, boolean registers, boolean expression) {
+    protected ASTJexlScript parse(JexlInfo info, JexlFeatures parsingf, String src, Scope scope) {
         final boolean cached = src.length() < cacheThreshold && cache != null;
+        final JexlFeatures features = parsingf != null? parsingf : DEFAULT_FEATURES;
+        final Source source = cached? new Source(features, src) : null;
         ASTJexlScript script = null;
-        if (cached) {
-            script = cache.get(src);
+        if (source != null) {
+            script = cache.get(source);
             if (script != null) {
                 Scope f = script.getScope();
                 if ((f == null && scope == null) || (f != null && f.equals(scope))) {
@@ -603,7 +641,7 @@ public class Engine extends JexlEngine {
         if (parsing.compareAndSet(false, true)) {
             try {
                 // lets parse
-                script = parser.parse(ninfo, src, scope, registers, expression);
+                script = parser.parse(ninfo, features, src, scope);
             } finally {
                 // no longer in use
                 parsing.set(false);
@@ -611,10 +649,10 @@ public class Engine extends JexlEngine {
         } else {
             // ...otherwise parser was in use, create a new temporary one
             Parser lparser = new Parser(new StringReader(";"));
-            script = lparser.parse(ninfo, src, scope, registers, expression);
+            script = lparser.parse(ninfo, features, src, scope);
         }
-        if (cached) {
-            cache.put(src, script);
+        if (source != null) {
+            cache.put(source, script);
         }
         return script;
     }

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=1816640&r1=1816639&r2=1816640&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 Wed Nov 29 16:27:04 2017
@@ -566,22 +566,23 @@ public class Interpreter extends Interpr
     @Override
     protected Object visit(ASTIfStatement node, Object data) {
         int n = 0;
+        final int numChildren = node.jjtGetNumChildren();
         try {
             Object result = null;
-            // first objectNode is the condition
-            Object expression = node.jjtGetChild(0).jjtAccept(this, null);
-            if (arithmetic.toBoolean(expression)) {
-                // first objectNode is true statement
-                n = 1;
-                result = node.jjtGetChild(n).jjtAccept(this, null);
-            } else {
-                    // if there is a false, execute it. false statement is the second
-                // objectNode
-                if (node.jjtGetNumChildren() == 3) {
-                    n = 2;
-                    result = node.jjtGetChild(n).jjtAccept(this, null);
+            // pairs of { conditions , 'then' statement }
+            for(int ifElse = 0; ifElse < (numChildren - 1); ifElse += 2) {
+                Object condition = node.jjtGetChild(ifElse).jjtAccept(this, null);
+                if (arithmetic.toBoolean(condition)) {
+                    // first objectNode is true statement
+                    return node.jjtGetChild(ifElse + 1).jjtAccept(this, null);
                 }
             }
+            //
+            if (numChildren % 2 == 1) {
+                // if there is an else, there are an odd number of childrenin the statement and it is the last child,
+                // execute it.
+                result = node.jjtGetChild(numChildren - 1).jjtAccept(this, null);
+            }
             return result;
         } catch (ArithmeticException xrt) {
             throw new JexlException(node.jjtGetChild(n), "if error", xrt);

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=1816640&r1=1816639&r2=1816640&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 Wed Nov 29 16:27:04 2017
@@ -50,6 +50,13 @@ public class Script implements JexlScrip
     protected int version;
 
     /**
+     * @return the script AST
+     */
+    protected ASTJexlScript getScript() {
+        return script;
+    }
+
+    /**
      * Do not let this be generally instantiated with a 'new'.
      *
      * @param engine the interpreter to evaluate the expression

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java?rev=1816640&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java Wed Nov 29 16:27:04 2017
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl3.internal;
+
+import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.jexl3.parser.ASTAddNode;
+import org.apache.commons.jexl3.parser.ASTAndNode;
+import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
+import org.apache.commons.jexl3.parser.ASTAnnotation;
+import org.apache.commons.jexl3.parser.ASTArguments;
+import org.apache.commons.jexl3.parser.ASTArrayAccess;
+import org.apache.commons.jexl3.parser.ASTArrayLiteral;
+import org.apache.commons.jexl3.parser.ASTAssignment;
+import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
+import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
+import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
+import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
+import org.apache.commons.jexl3.parser.ASTBlock;
+import org.apache.commons.jexl3.parser.ASTBreak;
+import org.apache.commons.jexl3.parser.ASTConstructorNode;
+import org.apache.commons.jexl3.parser.ASTContinue;
+import org.apache.commons.jexl3.parser.ASTDivNode;
+import org.apache.commons.jexl3.parser.ASTEQNode;
+import org.apache.commons.jexl3.parser.ASTERNode;
+import org.apache.commons.jexl3.parser.ASTEWNode;
+import org.apache.commons.jexl3.parser.ASTEmptyFunction;
+import org.apache.commons.jexl3.parser.ASTEmptyMethod;
+import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
+import org.apache.commons.jexl3.parser.ASTFalseNode;
+import org.apache.commons.jexl3.parser.ASTForeachStatement;
+import org.apache.commons.jexl3.parser.ASTFunctionNode;
+import org.apache.commons.jexl3.parser.ASTGENode;
+import org.apache.commons.jexl3.parser.ASTGTNode;
+import org.apache.commons.jexl3.parser.ASTIdentifier;
+import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
+import org.apache.commons.jexl3.parser.ASTIfStatement;
+import org.apache.commons.jexl3.parser.ASTJexlScript;
+import org.apache.commons.jexl3.parser.ASTJxltLiteral;
+import org.apache.commons.jexl3.parser.ASTLENode;
+import org.apache.commons.jexl3.parser.ASTLTNode;
+import org.apache.commons.jexl3.parser.ASTMapEntry;
+import org.apache.commons.jexl3.parser.ASTMapLiteral;
+import org.apache.commons.jexl3.parser.ASTMethodNode;
+import org.apache.commons.jexl3.parser.ASTModNode;
+import org.apache.commons.jexl3.parser.ASTMulNode;
+import org.apache.commons.jexl3.parser.ASTNENode;
+import org.apache.commons.jexl3.parser.ASTNEWNode;
+import org.apache.commons.jexl3.parser.ASTNRNode;
+import org.apache.commons.jexl3.parser.ASTNSWNode;
+import org.apache.commons.jexl3.parser.ASTNotNode;
+import org.apache.commons.jexl3.parser.ASTNullLiteral;
+import org.apache.commons.jexl3.parser.ASTNullpNode;
+import org.apache.commons.jexl3.parser.ASTNumberLiteral;
+import org.apache.commons.jexl3.parser.ASTOrNode;
+import org.apache.commons.jexl3.parser.ASTRangeNode;
+import org.apache.commons.jexl3.parser.ASTReference;
+import org.apache.commons.jexl3.parser.ASTReferenceExpression;
+import org.apache.commons.jexl3.parser.ASTReturnStatement;
+import org.apache.commons.jexl3.parser.ASTSWNode;
+import org.apache.commons.jexl3.parser.ASTSetAddNode;
+import org.apache.commons.jexl3.parser.ASTSetAndNode;
+import org.apache.commons.jexl3.parser.ASTSetDivNode;
+import org.apache.commons.jexl3.parser.ASTSetLiteral;
+import org.apache.commons.jexl3.parser.ASTSetModNode;
+import org.apache.commons.jexl3.parser.ASTSetMultNode;
+import org.apache.commons.jexl3.parser.ASTSetOrNode;
+import org.apache.commons.jexl3.parser.ASTSetSubNode;
+import org.apache.commons.jexl3.parser.ASTSetXorNode;
+import org.apache.commons.jexl3.parser.ASTSizeFunction;
+import org.apache.commons.jexl3.parser.ASTSizeMethod;
+import org.apache.commons.jexl3.parser.ASTStringLiteral;
+import org.apache.commons.jexl3.parser.ASTSubNode;
+import org.apache.commons.jexl3.parser.ASTTernaryNode;
+import org.apache.commons.jexl3.parser.ASTTrueNode;
+import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
+import org.apache.commons.jexl3.parser.ASTVar;
+import org.apache.commons.jexl3.parser.ASTWhileStatement;
+import org.apache.commons.jexl3.parser.JexlNode;
+import org.apache.commons.jexl3.parser.ParserVisitor;
+
+/**
+ * Fully abstract to avoid public interface exposition.
+ */
+public class ScriptVisitor extends ParserVisitor {
+    /**
+     * Visits all AST constituents of a JEXL expression.
+     * @param jscript the expression
+     * @param data some data context
+     * @return the visit result or null if jscript was not a Script implementation
+     */
+    public Object visitExpression (JexlExpression jscript, Object data) {
+        if (jscript instanceof Script) {
+            return ((Script) jscript).getScript().jjtAccept(this, data);
+        }
+        return null;
+    }
+
+    /**
+     * Visits all AST constituents of a JEXL script.
+     * @param jscript the expression
+     * @param data some data context
+     * @return the visit result or null if jscript was not a Script implementation
+     */
+    public Object visitScript(JexlScript jscript, Object data) {
+        if (jscript instanceof Script) {
+            return ((Script) jscript).getScript().jjtAccept(this, data);
+        }
+        return null;
+    }
+
+    /**
+     * Visits a node.
+     * Default implementation visits all its children.
+     * @param node the node to visit
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    protected Object visitNode(JexlNode node, Object data) {
+        return node.childrenAccept(this, data);
+    }
+
+    @Override
+    protected Object visit(ASTJexlScript node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBlock node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTIfStatement node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTWhileStatement node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTContinue node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBreak node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTForeachStatement node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTReturnStatement node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTAssignment node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTVar node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTReference node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTTernaryNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNullpNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTOrNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTAndNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBitwiseOrNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBitwiseXorNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBitwiseAndNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTEQNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNENode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTLTNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTGTNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTLENode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTGENode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTERNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNRNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSWNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNSWNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTEWNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNEWNode node, Object data) {
+
+        return visitNode(node, data);    }
+
+    @Override
+    protected Object visit(ASTAddNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSubNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTMulNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTDivNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTModNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTUnaryMinusNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTBitwiseComplNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNotNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTIdentifier node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNullLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTTrueNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTFalseNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTNumberLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTStringLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTExtendedLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTArrayLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTRangeNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTMapLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTMapEntry node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTEmptyFunction node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTEmptyMethod node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSizeFunction node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTFunctionNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTMethodNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSizeMethod node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTConstructorNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTArrayAccess node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTIdentifierAccess node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTArguments node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTReferenceExpression node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetAddNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetSubNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetMultNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetDivNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetModNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetAndNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetOrNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetXorNode node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTJxltLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTAnnotation node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTAnnotatedStatement node, Object data) {
+        return visitNode(node, data);
+    }
+}

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

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Source.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Source.java?rev=1816640&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Source.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Source.java Wed Nov 29 16:27:04 2017
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl3.internal;
+
+import org.apache.commons.jexl3.JexlFeatures;
+
+/**
+ * Maintains the set of allowed features associated with a script/expression source.
+ * <p>This is meant for caching scripts using their 'source' as key but still distinguishing
+ * scripts with different features and prevent false sharing.
+ */
+public final class Source {
+    /** The hash code, pre-computed for fast op. */
+    private final int hashCode;
+    /** The set of features. */
+    private final JexlFeatures features;
+    /** The actual source script/expression. */
+    private final String str;
+
+    /**
+     * Default constructor.
+     * @param theFeatures the features
+     * @param theStr the script source
+     */
+    Source(JexlFeatures theFeatures, String theStr) { // CSOFF: MagicNumber
+        this.features = theFeatures;
+        this.str = theStr;
+        int hash = 3;
+        hash = 37 * hash + features.hashCode();
+        hash = 37 * hash + str.hashCode() ;
+        this.hashCode = hash;
+    }
+
+    /**
+     * @return the length of the script source
+     */
+    int length() {
+        return str.length();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Source other = (Source) obj;
+        if (this.features != other.features
+            && (this.features == null || !this.features.equals(other.features))) {
+            return false;
+        }
+        if ((this.str == null) ? (other.str != null) : !this.str.equals(other.str)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+
+    /**
+     * @return the features associated with the source
+     */
+    public JexlFeatures getFeatures() {
+        return features;
+    }
+
+}

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

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java?rev=1816640&r1=1816639&r2=1816640&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 Wed Nov 29 16:27:04 2017
@@ -540,7 +540,7 @@ public final class TemplateEngine extend
         @Override
         protected TemplateExpression prepare(Interpreter interpreter) {
             String value = interpreter.interpret(node).toString();
-            JexlNode dnode = jexl.parse(node.jexlInfo(), value, null, false, noscript);
+            JexlNode dnode = jexl.parse(node.jexlInfo(), noscript, value, null);
             return new ImmediateExpression(value, dnode, this);
         }
 
@@ -800,7 +800,7 @@ public final class TemplateEngine extend
                             String src = strb.toString();
                             TemplateExpression iexpr = new ImmediateExpression(
                                     src,
-                                    jexl.parse(info.at(lineno, column), src, scope, false, noscript),
+                                    jexl.parse(info.at(lineno, column), noscript, src, scope),
                                     null);
                             builder.add(iexpr);
                             strb.delete(0, Integer.MAX_VALUE);
@@ -848,12 +848,12 @@ public final class TemplateEngine extend
                             if (nested) {
                                 dexpr = new NestedExpression(
                                         expr.substring(inested, column + 1),
-                                        jexl.parse(info.at(lineno, column), src, scope, false, noscript),
+                                        jexl.parse(info.at(lineno, column), noscript, src, scope),
                                         null);
                             } else {
                                 dexpr = new DeferredExpression(
                                         strb.toString(),
-                                        jexl.parse(info.at(lineno, column), src, scope, false, noscript),
+                                        jexl.parse(info.at(lineno, column), noscript, src, scope),
                                         null);
                             }
                             builder.add(dexpr);

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java?rev=1816640&r1=1816639&r2=1816640&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateScript.java Wed Nov 29 16:27:04 2017
@@ -96,7 +96,7 @@ public final class TemplateScript implem
             info = jxlt.getEngine().createInfo();
         }
         // allow lambda defining params
-        script = jxlt.getEngine().parse(info.at(0, 0), strb.toString(), scope, false, false).script();
+        script = jxlt.getEngine().parse(info.at(0, 0), false, strb.toString(), scope).script();
         scope = script.getScope();
         // create the exprs using the code frame for those appearing after the first block of code
         for (int b = 0; b < blocks.size(); ++b) {

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=1816640&r1=1816639&r2=1816640&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 Wed Nov 29 16:27:04 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.jexl3.parser;
 
+import org.apache.commons.jexl3.JexlFeatures;
 import org.apache.commons.jexl3.internal.Scope;
 import java.util.Map;
 
@@ -26,7 +27,9 @@ public class ASTJexlScript extends JexlN
     /** The script scope. */
     private Scope scope = null;
     /** The pragmas. */
-    Map<String, Object> pragmas = null;
+    private Map<String, Object> pragmas = null;
+    /** Features. */
+    private JexlFeatures features = null;
 
     public ASTJexlScript(int id) {
         super(id);
@@ -54,18 +57,34 @@ public class ASTJexlScript extends JexlN
     public Object jjtAccept(ParserVisitor visitor, Object data) {
         return visitor.visit(this, data);
     }
+      /**
+     * Sets this script pragmas.
+     * @param pragmas the pragmas
+     */
+    public void setPragmas(Map<String, Object> thePragmas) {
+        this.pragmas = thePragmas;
+    }
 
     /**
-     * Coerce this script as an expression (ie only one child) if necessary.
-     * @return true if the script was coerced, false otherwise
+     * @return this script pragmas.
      */
-    public boolean toExpression() {
-        if (jjtGetNumChildren() > 1) {
-            jjtSetChildren(new JexlNode[]{jjtGetChild(0)});
-            return true;
-        } else {
-            return false;
-        }
+    public Map<String, Object> getPragmas() {
+        return pragmas;
+    }
+
+    /**
+     * Sets this script features.
+     * @param theFeatures the features
+     */
+    public void setFeatures(JexlFeatures theFeatures) {
+        this.features = theFeatures;
+    }
+
+    /**
+     * @return this script scope
+     */
+    public JexlFeatures getFeatures() {
+        return features;
     }
 
     /**
@@ -84,13 +103,6 @@ public class ASTJexlScript extends JexlN
     }
 
     /**
-     * @return this script pragmas
-     */
-    public Map<String,Object> getPragmas() {
-        return pragmas;
-    }
-
-    /**
      * Creates an array of arguments by copying values up to the number of parameters.
      * @param values the argument values
      * @return the arguments array

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java?rev=1816640&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java Wed Nov 29 16:27:04 2017
@@ -0,0 +1,229 @@
+/*
+ * 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.parser;
+
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlFeatures;
+import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.internal.ScriptVisitor;
+/**
+ * Controls that a script only uses enabled features.
+ */
+public class FeatureController extends ScriptVisitor {
+    /** The set of features. */
+    private JexlFeatures features = null;
+
+    /**
+     * Creates a features controller .
+     */
+    public FeatureController(JexlFeatures features) {
+        this.features = features;
+    }
+
+    /**
+     * Sets the features to controlNode.
+     * @param fdesc the features
+     */
+    public void setFeatures(JexlFeatures fdesc) {
+        this.features = fdesc;
+    }
+
+    /**
+     * @return the controlled features
+     */
+    public JexlFeatures getFeatures() {
+        return features;
+    }
+
+    /**
+     * Perform the control on a node.
+     * <p>Note that controlNode() does *not* visit node children in this class.
+     * @param node the node to controlNode
+     * @throws JexlException.Feature if required feature is disabled
+     */
+    public void controlNode(JexlNode node) {
+        node.jjtAccept(this, null);
+    }
+
+    @Override
+    protected Object visitNode(JexlNode node, Object data) {
+        // no need to visit them since we close them one by one
+        return data;
+    }
+
+    /**
+     * Throws a feature exception.
+     * @param feature the feature code
+     * @param node    the node that caused it
+     */
+    public void throwFeatureException(int feature, JexlNode node) {
+        JexlInfo dbgInfo = node.jexlInfo();
+        throw new JexlException.Feature(dbgInfo, feature, "");
+    }
+
+    /**
+     * Checks whether a node is a string or an integer.
+     * @param child the child node
+     * @return true if string / integer, false otherwise
+     */
+    private boolean isArrayReferenceLiteral(JexlNode child) {
+        if (child instanceof ASTStringLiteral) {
+            return true;
+        }
+        if (child instanceof ASTNumberLiteral && ((ASTNumberLiteral) child).isInteger()) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected Object visit(ASTArrayAccess node, Object data) {
+        if (!features.supportsArrayReferenceExpr()) {
+            for (int i = 0; i < node.jjtGetNumChildren(); ++i) {
+                JexlNode child = node.jjtGetChild(i);
+                if (!isArrayReferenceLiteral(child)) {
+                    throwFeatureException(JexlFeatures.ARRAY_REF_EXPR, child);
+                }
+            }
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTWhileStatement node, Object data) {
+        if (!features.supportsLoops()) {
+            throwFeatureException(JexlFeatures.LOOP, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTForeachStatement node, Object data) {
+        if (!features.supportsLoops()) {
+            throwFeatureException(JexlFeatures.LOOP, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTConstructorNode node, Object data) {
+        if (!features.supportsNewInstance()) {
+            throwFeatureException(JexlFeatures.NEW_INSTANCE, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTMethodNode node, Object data) {
+        if (!features.supportsMethodCall()) {
+            throwFeatureException(JexlFeatures.METHOD_CALL, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTAnnotation node, Object data) {
+        if (!features.supportsAnnotation()) {
+            throwFeatureException(JexlFeatures.ANNOTATION, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTArrayLiteral node, Object data) {
+        if (!features.supportsStructuredLiteral()) {
+            throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTMapLiteral node, Object data) {
+        if (!features.supportsStructuredLiteral()) {
+            throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTSetLiteral node, Object data) {
+        if (!features.supportsStructuredLiteral()) {
+            throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTRangeNode node, Object data) {
+        if (!features.supportsStructuredLiteral()) {
+            throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
+        }
+        return data;
+    }
+
+    private Object controlSideEffect(JexlNode node, Object data) {
+        JexlNode lv = node.jjtGetChild(0);
+        if (!features.supportsSideEffectGlobal() && lv.isGlobalVar()) {
+            throwFeatureException(JexlFeatures.SIDE_EFFECT_GLOBAL, lv);
+        }
+        if (!features.supportsSideEffect()) {
+            throwFeatureException(JexlFeatures.SIDE_EFFECT, lv);
+        }
+        return data;
+    }
+
+    @Override
+    protected Object visit(ASTAssignment node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetAddNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetMultNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetDivNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetAndNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetOrNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetXorNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+    @Override
+    protected Object visit(ASTSetSubNode node, Object data) {
+        return controlSideEffect(node, data);
+    }
+
+}

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