JexlFeatures.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.jexl3;

  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.Objects;
  23. import java.util.Set;
  24. import java.util.TreeSet;
  25. import java.util.function.Predicate;

  26. /**
  27.  * A set of language feature options.
  28.  * <p>
  29.  * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
  30.  * subclass of JexlException.Parsing) when disabled.
  31.  * </p>
  32.  * <p>It is recommended to be explicit in choosing the features you need rather than rely on the default
  33.  * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} and {@link JexlFeatures#createAll()}
  34.  * are the recommended starting points to selectively enable or disable chosen features.</p>
  35.  * <ul>
  36.  * <li>Registers: register syntax (#number), used internally for {g,s}etProperty
  37.  * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
  38.  * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
  39.  * <li>Lexical: lexical scope, prevents redefining local variables
  40.  * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
  41.  * <li>Side Effect : assigning/modifying values on any variables or left-value
  42.  * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
  43.  * <li>New Instance: creating an instance using new(...)
  44.  * <li>Loops: loop constructs (while(true), for(...))
  45.  * <li>Lambda: function definitions (()-&gt;{...}, function(...) ).
  46.  * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
  47.  * - including namespace prefixes - available
  48.  * <li>Structured literals: arrays, lists, maps, sets, ranges
  49.  * <li>Pragma: pragma construct as in <code>#pragma x y</code>
  50.  * <li>Annotation: @annotation statement;
  51.  * <li>Thin-arrow: use the thin-arrow, ie <code>-&gt;</code> for lambdas as in <code>x -&gt; x + x</code>
  52.  * <li>Fat-arrow: use the  fat-arrow, ie <code>=&gt;</code> for lambdas as in <code>x =&gt; x + x</code>
  53.  * <li>Namespace pragma: whether the <code>#pragma jexl.namespace.ns namespace</code> syntax is allowed</li>
  54.  * <li>Import pragma: whether the <code>#pragma jexl.import fully.qualified.class.name</code> syntax is allowed</li>
  55.  * <li>Comparator names: whether the comparator operator names can be used (as in <code>gt</code> for &gt;,
  56.  * <code>lt</code> for &lt;, ...)</li>
  57.  * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
  58.  * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
  59.  * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
  60.  * </ul>
  61.  * @since 3.2
  62.  */
  63. public final class JexlFeatures {
  64.     /** The false predicate. */
  65.     public static final Predicate<String> TEST_STR_FALSE = s -> false;
  66.     /** Te feature names (for toString()). */
  67.     private static final String[] F_NAMES = {
  68.         "register", "reserved variable", "local variable", "assign/modify",
  69.         "global assign/modify", "array reference", "create instance", "loop", "function",
  70.         "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
  71.         "thin-arrow", "fat-arrow", "namespace pragma", "import pragma", "comparator names", "pragma anywhere",
  72.         "const capture"
  73.     };
  74.     /** Registers feature ordinal. */
  75.     private static final int REGISTER = 0;
  76.     /** Reserved future feature ordinal (unused as of 3.3.1). */
  77.     public static final int RESERVED = 1;
  78.     /** Locals feature ordinal. */
  79.     public static final int LOCAL_VAR = 2;
  80.     /** Side effects feature ordinal. */
  81.     public static final int SIDE_EFFECT = 3;
  82.     /** Global side effects feature ordinal. */
  83.     public static final int SIDE_EFFECT_GLOBAL = 4;
  84.     /** Expressions allowed in array reference ordinal. */
  85.     public static final int ARRAY_REF_EXPR = 5;
  86.     /** New-instance feature ordinal. */
  87.     public static final int NEW_INSTANCE = 6;
  88.     /** Loops feature ordinal. */
  89.     public static final int LOOP = 7;
  90.     /** Lambda feature ordinal. */
  91.     public static final int LAMBDA = 8;
  92.     /** Lambda feature ordinal. */
  93.     public static final int METHOD_CALL = 9;
  94.     /** Structured literal feature ordinal. */
  95.     public static final int STRUCTURED_LITERAL = 10;
  96.     /** Pragma feature ordinal. */
  97.     public static final int PRAGMA = 11;
  98.     /** Annotation feature ordinal. */
  99.     public static final int ANNOTATION = 12;
  100.     /** Script feature ordinal. */
  101.     public static final int SCRIPT = 13;
  102.     /** Lexical feature ordinal. */
  103.     public static final int LEXICAL = 14;
  104.     /** Lexical shade feature ordinal. */
  105.     public static final int LEXICAL_SHADE = 15;
  106.     /** Thin-arrow lambda syntax. */
  107.     public static final int THIN_ARROW = 16;
  108.     /** Fat-arrow lambda syntax. */
  109.     public static final int FAT_ARROW = 17;
  110.     /** Namespace pragma feature ordinal. */
  111.     public static final int NS_PRAGMA = 18;
  112.     /** Import pragma feature ordinal. */
  113.     public static final int IMPORT_PRAGMA = 19;
  114.     /** Comparator names (legacy) syntax. */
  115.     public static final int COMPARATOR_NAMES = 20;
  116.     /** The pragma anywhere feature ordinal. */
  117.     public static final int PRAGMA_ANYWHERE = 21;
  118.     /** Captured variables are const. */
  119.     public static final int CONST_CAPTURE = 22;
  120.     /**
  121.      * All features.
  122.      * N.B. ensure this is updated if additional features are added.
  123.      */
  124.     private static final long ALL_FEATURES = (1L << CONST_CAPTURE + 1) - 1L; // MUST REMAIN PRIVATE
  125.     /**
  126.      * The default features flag mask.
  127.      * <p>Meant for compatibility with scripts written before 3.3.1</p>
  128.      */
  129.     private static final long DEFAULT_FEATURES = // MUST REMAIN PRIVATE
  130.         1L << LOCAL_VAR
  131.         | 1L << SIDE_EFFECT
  132.         | 1L << SIDE_EFFECT_GLOBAL
  133.         | 1L << ARRAY_REF_EXPR
  134.         | 1L << NEW_INSTANCE
  135.         | 1L << LOOP
  136.         | 1L << LAMBDA
  137.         | 1L << METHOD_CALL
  138.         | 1L << STRUCTURED_LITERAL
  139.         | 1L << PRAGMA
  140.         | 1L << ANNOTATION
  141.         | 1L << SCRIPT
  142.         | 1L << THIN_ARROW
  143.         | 1L << NS_PRAGMA
  144.         | 1L << IMPORT_PRAGMA
  145.         | 1L << COMPARATOR_NAMES
  146.         | 1L << PRAGMA_ANYWHERE;
  147.     /**
  148.      * The canonical scripting (since 3.3.1) features flag mask based on the original default.
  149.      * <p>Adds lexical, lexical-shade and const-capture but removes comparator-names and pragma-anywhere</p>
  150.      */
  151.     private static final long SCRIPT_FEATURES = // MUST REMAIN PRIVATE
  152.         ( DEFAULT_FEATURES
  153.         | 1L << LEXICAL
  154.         | 1L << LEXICAL_SHADE
  155.         | 1L << CONST_CAPTURE ) // these parentheses are necessary :-)
  156.         & ~(1L << COMPARATOR_NAMES)
  157.         & ~(1L << PRAGMA_ANYWHERE);

  158.     /**
  159.      * Protected future syntactic elements.
  160.      * <p><em>throw, switch, case, default, class, instanceof, jexl, $jexl</em></p>
  161.      * @since 3.3.1
  162.      */
  163.     private static final Set<String> RESERVED_WORDS =
  164.         Collections.unmodifiableSet(
  165.             new HashSet<>(Arrays.asList(
  166.                 "switch", "case", "default", "class", "jexl", "$jexl")));

  167.     /*
  168.      * *WARNING*
  169.      * Static fields may be inlined by the Java compiler, so their _values_ effectively form part of the external API.
  170.      * Classes that reference them need to be recompiled to pick up new values.
  171.      * This means that changes in value are not binary compatible.
  172.      * Such fields must be private or problems may occur.
  173.      */

  174.      /**
  175.      * Creates an all features enabled set.
  176.      * @return a new instance of all features set
  177.      * @since 3.3.1
  178.      */
  179.     public static JexlFeatures createAll() {
  180.         return new JexlFeatures(ALL_FEATURES, null, null);
  181.     }

  182.     /**
  183.      * Creates a default features set suitable for basic but complete scripting needs.
  184.      * <p>Maximizes compatibility with older version scripts (before 3.3), new projects should
  185.      * use {@link JexlFeatures#createScript()} or equivalent features as a base.</p>
  186.      * <p>The following scripting features are enabled:</p>
  187.      * <ul>
  188.      *   <li>local variable, {@link JexlFeatures#supportsLocalVar()}</li>
  189.      *   <li>side effect, {@link JexlFeatures#supportsSideEffect()}</li>
  190.      *   <li>global side effect, {@link JexlFeatures#supportsSideEffectGlobal()}</li>
  191.      *   <li>array reference expression, {@link JexlFeatures#supportsStructuredLiteral()}</li>
  192.      *   <li>new instance, {@link JexlFeatures#supportsNewInstance()} </li>
  193.      *   <li>loop, {@link JexlFeatures#supportsLoops()}</li>
  194.      *   <li>lambda, {@link JexlFeatures#supportsLambda()}</li>
  195.      *   <li>method call, {@link JexlFeatures#supportsMethodCall()}</li>
  196.      *   <li>structured literal, {@link JexlFeatures#supportsStructuredLiteral()}</li>
  197.      *   <li>pragma, {@link JexlFeatures#supportsPragma()}</li>
  198.      *   <li>annotation, {@link JexlFeatures#supportsAnnotation()}</li>
  199.      *   <li>script, {@link JexlFeatures#supportsScript()}</li>
  200.      *   <li>comparator names,  {@link JexlFeatures#supportsComparatorNames()}</li>
  201.      *   <li>namespace pragma,  {@link JexlFeatures#supportsNamespacePragma()}</li>
  202.      *   <li>import pragma, {@link JexlFeatures#supportsImportPragma()}</li>
  203.      *   <li>pragma anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
  204.      * </ul>
  205.      * @return a new instance of a default scripting features set
  206.      * @since 3.3.1
  207.      */
  208.     public static JexlFeatures createDefault() {
  209.         return new JexlFeatures(DEFAULT_FEATURES, null, null);
  210.     }

  211.     /**
  212.      * Creates an empty feature set.
  213.      * <p>This is the strictest base-set since no feature is allowed, suitable as-is only
  214.      * for the simplest expressions.</p>
  215.      * @return a new instance of an empty features set
  216.      * @since 3.3.1
  217.      */
  218.     public static JexlFeatures createNone() {
  219.         return new JexlFeatures(0L, null, null);
  220.     }

  221.     /**
  222.      * The modern scripting features set.
  223.      * <p>This is the recommended set for new projects.</p>
  224.      * <p>All default features with the following differences:</p>
  225.      * <ul>
  226.      * <li><em>disable</em> pragma-anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
  227.      * <li><em>disable</em> comparator-names, {@link JexlFeatures#supportsComparatorNames()}</li>
  228.      * <li><em>enable</em> lexical, {@link JexlFeatures#isLexical()}</li>
  229.      * <li><em>enable</em> lexical-shade, {@link JexlFeatures#isLexicalShade()} </li>
  230.      * <li><em>enable</em> const-capture, {@link JexlFeatures#supportsConstCapture()}</li>
  231.      * </ul>
  232.      * <p>It also adds a set of reserved words to enable future unencumbered syntax evolution:
  233.      * <em>try, catch, throw, finally, switch, case, default, class, instanceof</em>
  234.      * </p>
  235.      * @return a new instance of a modern scripting features set
  236.      * @since 3.3.1
  237.      */
  238.     public static JexlFeatures createScript() {
  239.         return new JexlFeatures(SCRIPT_FEATURES, RESERVED_WORDS, null);
  240.     }

  241.     /**
  242.      * The text corresponding to a feature code.
  243.      * @param feature the feature number
  244.      * @return the feature name
  245.      */
  246.     public static String stringify(final int feature) {
  247.         return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
  248.     }

  249.     /** The feature flags. */
  250.     private long flags;

  251.     /** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */
  252.     private Set<String> reservedNames;

  253.     /** The namespace names. */
  254.     private Predicate<String> nameSpaces;

  255.     /**
  256.      * Creates default instance, equivalent to the result of calling the preferred alternative
  257.      * {@link JexlFeatures#createDefault()}
  258.      */
  259.     public JexlFeatures() {
  260.         this(DEFAULT_FEATURES, null, null);
  261.     }

  262.     /**
  263.      * Copy constructor.
  264.      * @param features the feature to copy from
  265.      */
  266.     public JexlFeatures(final JexlFeatures features) {
  267.         this(features.flags, features.reservedNames, features.nameSpaces);
  268.     }

  269.     /**
  270.      * An all member constructor for derivation.
  271.      * <p>Not respecting immutability or thread-safety constraints for this class constructor arguments will
  272.      * likely result in unexpected behavior.</p>
  273.      * @param f flag
  274.      * @param r reserved variable names; must be an immutable Set or thread-safe (concurrent or synchronized set)
  275.      * @param n namespace predicate; must be stateless or thread-safe
  276.      */
  277.     protected JexlFeatures(final long f, final Set<String> r, final Predicate<String> n) {
  278.         this.flags = f;
  279.         this.reservedNames = r == null? Collections.emptySet() : r;
  280.         this.nameSpaces = n == null? TEST_STR_FALSE : n;
  281.     }

  282.     /**
  283.      * Sets whether annotation constructs are enabled.
  284.      * <p>
  285.      * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
  286.      * will throw a parsing exception.
  287.      * </p>
  288.      * @param flag true to enable, false to disable
  289.      * @return this features instance
  290.      */
  291.     public JexlFeatures annotation(final boolean flag) {
  292.         setFeature(ANNOTATION, flag);
  293.         return this;
  294.     }

  295.     /**
  296.      * Sets whether array references expressions are enabled.
  297.      * <p>
  298.      * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
  299.      * will throw a parsing exception;
  300.      * </p>
  301.      * @param flag true to enable, false to disable
  302.      * @return this features instance
  303.      */
  304.     public JexlFeatures arrayReferenceExpr(final boolean flag) {
  305.         setFeature(ARRAY_REF_EXPR, flag);
  306.         return this;
  307.     }

  308.     /**
  309.      * Sets whether the legacy comparison operator names syntax is enabled.
  310.      * <p>
  311.      * When disabled, comparison operators names (eq;ne;le;lt;ge;gt)
  312.      * will be treated as plain identifiers.
  313.      * </p>
  314.      * @param flag true to enable, false to disable
  315.      * @return this features instance
  316.      * @since 3.3
  317.      */
  318.     public JexlFeatures comparatorNames(final boolean flag) {
  319.         setFeature(COMPARATOR_NAMES, flag);
  320.         return this;
  321.     }

  322.     /**
  323.      * Sets whether lambda captured-variables are const or not.
  324.      * <p>
  325.      * When disabled, lambda-captured variables are implicitly converted to read-write local variable (let),
  326.      * when enabled, those are implicitly converted to read-only local variables (const).
  327.      * </p>
  328.      * @param flag true to enable, false to disable
  329.      * @return this features instance
  330.      */
  331.     public JexlFeatures constCapture(final boolean flag) {
  332.         setFeature(CONST_CAPTURE, flag);
  333.         return this;
  334.     }

  335.     @Override
  336.     public boolean equals(final Object obj) {
  337.         if (this == obj) {
  338.             return true;
  339.         }
  340.         if (obj == null) {
  341.             return false;
  342.         }
  343.         if (getClass() != obj.getClass()) {
  344.             return false;
  345.         }
  346.         final JexlFeatures other = (JexlFeatures) obj;
  347.         if (this.flags != other.flags) {
  348.             return false;
  349.         }
  350.         if (this.nameSpaces != other.nameSpaces) {
  351.             return false;
  352.         }
  353.         if (!Objects.equals(this.reservedNames, other.reservedNames)) {
  354.             return false;
  355.         }
  356.         return true;
  357.     }

  358.     /**
  359.      * Sets whether fat-arrow lambda syntax is enabled.
  360.      * <p>
  361.      * When disabled, parsing a script/expression using syntactic fat-arrow (=&lt;)
  362.      * will throw a parsing exception.
  363.      * </p>
  364.      * @param flag true to enable, false to disable
  365.      * @return this features instance
  366.      * @since 3.3
  367.      */
  368.     public JexlFeatures fatArrow(final boolean flag) {
  369.         setFeature(FAT_ARROW, flag);
  370.         return this;
  371.     }

  372.     /**
  373.      * Gets a feature flag value.
  374.      * @param feature feature ordinal
  375.      * @return true if on, false if off
  376.      */
  377.     private boolean getFeature(final int feature) {
  378.         return (flags & 1L << feature) != 0L;
  379.     }

  380.     /**
  381.      * @return these features&quot;s flags
  382.      */
  383.     public long getFlags() {
  384.         return flags;
  385.     }

  386.     /**
  387.      * @return the (unmodifiable) set of reserved names.
  388.      */
  389.     public Set<String> getReservedNames() {
  390.         return reservedNames;
  391.     }

  392.     @Override
  393.     public int hashCode() { //CSOFF: MagicNumber
  394.         int hash = 3;
  395.         hash = 53 * hash + (int) (this.flags ^ this.flags >>> 32);
  396.         hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
  397.         return hash;
  398.     }

  399.     /**
  400.      * Sets whether import pragma constructs are enabled.
  401.      * <p>
  402.      * When disabled, parsing a script/expression using syntactic import pragma constructs
  403.      * (#pragma jexl.import....) will throw a parsing exception.
  404.      * </p>
  405.      * @param flag true to enable, false to disable
  406.      * @return this features instance
  407.      * @since 3.3
  408.      */
  409.     public JexlFeatures importPragma(final boolean flag) {
  410.         setFeature(IMPORT_PRAGMA, flag);
  411.         return this;
  412.     }

  413.     /** @return whether lexical scope feature is enabled */
  414.     public boolean isLexical() {
  415.         return getFeature(LEXICAL);
  416.     }

  417.     /** @return whether lexical shade feature is enabled */
  418.     public boolean isLexicalShade() {
  419.         return getFeature(LEXICAL_SHADE);
  420.     }

  421.     /**
  422.      * Checks whether a name is reserved.
  423.      * @param name the name to check
  424.      * @return true if reserved, false otherwise
  425.      */
  426.     public boolean isReservedName(final String name) {
  427.         return name != null && reservedNames.contains(name);
  428.     }

  429.     /**
  430.      * Sets whether lambda/function constructs are enabled.
  431.      * <p>
  432.      * When disabled, parsing a script/expression using syntactic lambda constructs (-&gt;,function)
  433.      * will throw a parsing exception.
  434.      * </p>
  435.      * @param flag true to enable, false to disable
  436.      * @return this features instance
  437.      */
  438.     public JexlFeatures lambda(final boolean flag) {
  439.         setFeature(LAMBDA, flag);
  440.         return this;
  441.     }

  442.     /**
  443.      * Sets whether syntactic lexical mode is enabled.
  444.      *
  445.      * @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping
  446.      * @return this features instance
  447.      */
  448.     public JexlFeatures lexical(final boolean flag) {
  449.         setFeature(LEXICAL, flag);
  450.         if (!flag) {
  451.             setFeature(LEXICAL_SHADE, false);
  452.         }
  453.         return this;
  454.     }

  455.     /**
  456.      * Sets whether syntactic lexical shade is enabled.
  457.      *
  458.      * @param flag true means syntactic lexical shade is in effect and implies lexical scope
  459.      * @return this features instance
  460.      */
  461.     public JexlFeatures lexicalShade(final boolean flag) {
  462.         setFeature(LEXICAL_SHADE, flag);
  463.         if (flag) {
  464.             setFeature(LEXICAL, true);
  465.         }
  466.         return this;
  467.     }

  468.     /**
  469.      * Sets whether local variables are enabled.
  470.      * <p>
  471.      * When disabled, parsing a script/expression using a local variable or parameter syntax
  472.      * will throw a parsing exception.
  473.      * </p>
  474.      * @param flag true to enable, false to disable
  475.      * @return this features instance
  476.      */
  477.     public JexlFeatures localVar(final boolean flag) {
  478.         setFeature(LOCAL_VAR, flag);
  479.         return this;
  480.     }

  481.     /**
  482.      * Sets whether looping constructs are enabled.
  483.      * <p>
  484.      * When disabled, parsing a script/expression using syntactic looping constructs (for,while)
  485.      * will throw a parsing exception.
  486.      * </p>
  487.      * @param flag true to enable, false to disable
  488.      * @return this features instance
  489.      */
  490.     public JexlFeatures loops(final boolean flag) {
  491.         setFeature(LOOP, flag);
  492.         return this;
  493.     }

  494.     /**
  495.      * Sets whether method calls expressions are enabled.
  496.      * <p>
  497.      * When disabled, parsing a script/expression using 'obj.method()'
  498.      * will throw a parsing exception;
  499.      * </p>
  500.      * @param flag true to enable, false to disable
  501.      * @return this features instance
  502.      */
  503.     public JexlFeatures methodCall(final boolean flag) {
  504.         setFeature(METHOD_CALL, flag);
  505.         return this;
  506.     }

  507.     /**
  508.      * Sets whether namespace pragma constructs are enabled.
  509.      * <p>
  510.      * When disabled, parsing a script/expression using syntactic namespace pragma constructs
  511.      * (#pragma jexl.namespace....) will throw a parsing exception.
  512.      * </p>
  513.      * @param flag true to enable, false to disable
  514.      * @return this features instance
  515.      * @since 3.3
  516.      */
  517.     public JexlFeatures namespacePragma(final boolean flag) {
  518.         setFeature(NS_PRAGMA, flag);
  519.         return this;
  520.     }

  521.     /**
  522.      * @return the declared namespaces test.
  523.      */
  524.     public Predicate<String> namespaceTest() {
  525.         return nameSpaces;
  526.     }

  527.     /**
  528.      * Sets a test to determine namespace declaration.
  529.      * @param names the name predicate
  530.      * @return this features instance
  531.      */
  532.     public JexlFeatures namespaceTest(final Predicate<String> names) {
  533.         nameSpaces = names == null ? TEST_STR_FALSE : names;
  534.         return this;
  535.     }

  536.     /**
  537.      * Sets whether creating new instances is enabled.
  538.      * <p>
  539.      * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
  540.      * using a class as functor will fail at runtime.
  541.      * </p>
  542.      * @param flag true to enable, false to disable
  543.      * @return this features instance
  544.      */
  545.     public JexlFeatures newInstance(final boolean flag) {
  546.         setFeature(NEW_INSTANCE, flag);
  547.         return this;
  548.     }

  549.     /**
  550.      * Sets whether pragma constructs are enabled.
  551.      * <p>
  552.      * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
  553.      * will throw a parsing exception.
  554.      * </p>
  555.      * @param flag true to enable, false to disable
  556.      * @return this features instance
  557.      */
  558.     public JexlFeatures pragma(final boolean flag) {
  559.         setFeature(PRAGMA, flag);
  560.         if (!flag) {
  561.             setFeature(NS_PRAGMA, false);
  562.             setFeature(IMPORT_PRAGMA, false);
  563.         }
  564.         return this;
  565.     }

  566.     /**
  567.      * Sets whether pragma constructs can appear anywhere in the code.
  568.      *
  569.      * @param flag true to enable, false to disable
  570.      * @return this features instance
  571.      * @since 3.3
  572.      */
  573.     public JexlFeatures pragmaAnywhere(final boolean flag) {
  574.         setFeature(PRAGMA_ANYWHERE, flag);
  575.         return this;
  576.     }

  577.     /**
  578.      * Sets whether register are enabled.
  579.      * <p>
  580.      * This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
  581.      * </p>
  582.      * <p>
  583.      * When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
  584.      * </p>
  585.      * @param flag true to enable, false to disable
  586.      * @return this features instance
  587.      */
  588.     public JexlFeatures register(final boolean flag) {
  589.         setFeature(REGISTER, flag);
  590.         return this;
  591.     }

  592.     /**
  593.      * Sets a collection of reserved r precluding those to be used as local variables or parameter r.
  594.      * @param names the r to reserve
  595.      * @return this features instance
  596.      */
  597.     public JexlFeatures reservedNames(final Collection<String> names) {
  598.         if (names == null || names.isEmpty()) {
  599.             reservedNames = Collections.emptySet();
  600.         } else {
  601.             reservedNames = Collections.unmodifiableSet(new TreeSet<>(names));
  602.         }
  603.         return this;
  604.     }

  605.     /**
  606.      * Sets whether scripts constructs are enabled.
  607.      * <p>
  608.      * When disabled, parsing a script using syntactic script constructs (statements, ...)
  609.      * will throw a parsing exception.
  610.      * </p>
  611.      * @param flag true to enable, false to disable
  612.      * @return this features instance
  613.      */
  614.     public JexlFeatures script(final boolean flag) {
  615.         setFeature(SCRIPT, flag);
  616.         return this;
  617.     }

  618.     /**
  619.      * Sets a feature flag.
  620.      * @param feature the feature ordinal
  621.      * @param flag    turn-on, turn off
  622.      */
  623.     private void setFeature(final int feature, final boolean flag) {
  624.         if (flag) {
  625.             flags |= 1L << feature;
  626.         } else {
  627.             flags &= ~(1L << feature);
  628.         }
  629.     }

  630.     /**
  631.      * Sets whether side effect expressions are enabled.
  632.      * <p>
  633.      * When disabled, parsing a script/expression using syntactical constructs modifying variables
  634.      * or members will throw a parsing exception.
  635.      * </p>
  636.      * @param flag true to enable, false to disable
  637.      * @return this features instance
  638.      */
  639.     public JexlFeatures sideEffect(final boolean flag) {
  640.         setFeature(SIDE_EFFECT, flag);
  641.         return this;
  642.     }

  643.     /**
  644.      * Sets whether side effect expressions on global variables (aka non-local) are enabled.
  645.      * <p>
  646.      * When disabled, parsing a script/expression using syntactical constructs modifying variables
  647.      * <em>including all potentially ant-ish variables</em> will throw a parsing exception.
  648.      * </p>
  649.      * @param flag true to enable, false to disable
  650.      * @return this features instance
  651.      */
  652.     public JexlFeatures sideEffectGlobal(final boolean flag) {
  653.         setFeature(SIDE_EFFECT_GLOBAL, flag);
  654.         return this;
  655.     }

  656.     /**
  657.      * Sets whether array/map/set literal expressions are enabled.
  658.      * <p>
  659.      * When disabled, parsing a script/expression creating one of these literals
  660.      * will throw a parsing exception;
  661.      * </p>
  662.      * @param flag true to enable, false to disable
  663.      * @return this features instance
  664.      */
  665.     public JexlFeatures structuredLiteral(final boolean flag) {
  666.         setFeature(STRUCTURED_LITERAL, flag);
  667.         return this;
  668.     }

  669.     /**
  670.      * @return true if annotation are enabled, false otherwise
  671.      */
  672.     public boolean supportsAnnotation() {
  673.         return getFeature(ANNOTATION);
  674.     }

  675.     /**
  676.      * @return true if array references can contain method call expressions, false otherwise
  677.      */
  678.     public boolean supportsArrayReferenceExpr() {
  679.         return getFeature(ARRAY_REF_EXPR);
  680.     }

  681.     /**
  682.      * @return true if legacy comparison operator names syntax is enabled, false otherwise
  683.      * @since 3.3
  684.      */
  685.     public boolean supportsComparatorNames() {
  686.         return getFeature(COMPARATOR_NAMES);
  687.     }

  688.     /**
  689.      * @return true if lambda captured-variables are const, false otherwise
  690.      */
  691.     public boolean supportsConstCapture() {
  692.         return getFeature(CONST_CAPTURE);
  693.     }

  694.     /**
  695.      *
  696.      * @return true if expressions (aka not scripts) are enabled, false otherwise
  697.      */
  698.     public boolean supportsExpression() {
  699.         return !getFeature(SCRIPT);
  700.     }

  701.     /**
  702.      * @return true if fat-arrow lambda syntax is enabled, false otherwise
  703.      * @since 3.3
  704.      */
  705.     public boolean supportsFatArrow() {
  706.         return getFeature(FAT_ARROW);
  707.     }

  708.     /**
  709.      * @return true if import pragma are enabled, false otherwise
  710.      * @since 3.3
  711.      */
  712.     public boolean supportsImportPragma() {
  713.         return getFeature(IMPORT_PRAGMA);
  714.     }

  715.     /**
  716.      * @return true if lambda are enabled, false otherwise
  717.      */
  718.     public boolean supportsLambda() {
  719.         return getFeature(LAMBDA);
  720.     }

  721.     /**
  722.      * @return true if local variables syntax is enabled
  723.      */
  724.     public boolean supportsLocalVar() {
  725.         return getFeature(LOCAL_VAR);
  726.     }

  727.     /**
  728.      * @return true if loops are enabled, false otherwise
  729.      */
  730.     public boolean supportsLoops() {
  731.         return getFeature(LOOP);
  732.     }

  733.     /**
  734.      * @return true if array references can contain expressions, false otherwise
  735.      */
  736.     public boolean supportsMethodCall() {
  737.         return getFeature(METHOD_CALL);
  738.     }
  739.     /**
  740.      * @return true if namespace pragma are enabled, false otherwise
  741.      * @since 3.3
  742.      */
  743.     public boolean supportsNamespacePragma() {
  744.         return getFeature(NS_PRAGMA);
  745.     }

  746.     /**
  747.      * @return true if creating new instances is enabled, false otherwise
  748.      */
  749.     public boolean supportsNewInstance() {
  750.         return getFeature(NEW_INSTANCE);
  751.     }

  752.     /**
  753.      * @return true if namespace pragma are enabled, false otherwise
  754.      */
  755.     public boolean supportsPragma() {
  756.         return getFeature(PRAGMA);
  757.     }

  758.     /**
  759.      * @return true if pragma constructs can appear anywhere in the code, false otherwise
  760.      * @since 3.3
  761.      */
  762.     public boolean supportsPragmaAnywhere() {
  763.         return getFeature(PRAGMA_ANYWHERE);
  764.     }

  765.     /**
  766.      * @return true if register syntax is enabled
  767.      */
  768.     public boolean supportsRegister() {
  769.         return getFeature(REGISTER);
  770.     }

  771.     /**
  772.      * @return true if scripts are enabled, false otherwise
  773.      */
  774.     public boolean supportsScript() {
  775.         return getFeature(SCRIPT);
  776.     }

  777.     /**
  778.      * @return true if side effects are enabled, false otherwise
  779.      */
  780.     public boolean supportsSideEffect() {
  781.         return getFeature(SIDE_EFFECT);
  782.     }

  783.     /**
  784.      * @return true if global variables can be assigned
  785.      */
  786.     public boolean supportsSideEffectGlobal() {
  787.         return getFeature(SIDE_EFFECT_GLOBAL);
  788.     }

  789.     /**
  790.      * @return true if array/map/set literal expressions are supported, false otherwise
  791.      */
  792.     public boolean supportsStructuredLiteral() {
  793.         return getFeature(STRUCTURED_LITERAL);
  794.     }

  795.     /**
  796.      * @return true if thin-arrow lambda syntax is enabled, false otherwise
  797.      * @since 3.3
  798.      */
  799.     public boolean supportsThinArrow() {
  800.         return getFeature(THIN_ARROW);
  801.     }

  802.     /**
  803.      * Sets whether thin-arrow lambda syntax is enabled.
  804.      * <p>
  805.      * When disabled, parsing a script/expression using syntactic thin-arrow (-&lt;)
  806.      * will throw a parsing exception.
  807.      * </p>
  808.      * @param flag true to enable, false to disable
  809.      * @return this features instance
  810.      * @since 3.3
  811.      */
  812.     public JexlFeatures thinArrow(final boolean flag) {
  813.         setFeature(THIN_ARROW, flag);
  814.         return this;
  815.     }
  816. }