    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.jexl3;
    018
    019import java.util.Arrays;
    020import java.util.Collection;
    021import java.util.Collections;
    022import java.util.HashSet;
    023import java.util.Objects;
    024import java.util.Set;
    025import java.util.TreeSet;
    026import java.util.function.Predicate;
    027
    028/**
    029 * A set of language feature options.
    030 * <p>
    031 * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
    032 * subclass of JexlException.Parsing) when disabled.
    033 * </p>
    034 * <p>It is recommended to be explicit in choosing the features you need rather than rely on the default
    035 * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} and {@link JexlFeatures#createAll()}
    036 * are the recommended starting points to selectively enable or disable chosen features.</p>
    037 * <ul>
    038 * <li>Registers: register syntax (#number), used internally for {g,s}etProperty
    039 * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
    040 * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
    041 * <li>Lexical: lexical scope, prevents redefining local variables
    042 * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
    043 * <li>Side Effect : assigning/modifying values on any variables or left-value
    044 * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
    045 * <li>New Instance: creating an instance using new(...)
    046 * <li>Loops: loop constructs (while(true), for(...))
    047 * <li>Lambda: function definitions (()-&gt;{...}, function(...) ).
    048 * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
    049 * - including namespace prefixes - available
    050 * <li>Structured literals: arrays, lists, maps, sets, ranges
    051 * <li>Pragma: pragma construct as in <code>#pragma x y</code>
    052 * <li>Annotation: @annotation statement;
    053 * <li>Thin-arrow: use the thin-arrow, ie <code>-&gt;</code> for lambdas as in <code>x -&gt; x + x</code>
    054 * <li>Fat-arrow: use the  fat-arrow, ie <code>=&gt;</code> for lambdas as in <code>x =&gt; x + x</code>
    055 * <li>Namespace pragma: whether the <code>#pragma jexl.namespace.ns namespace</code> syntax is allowed</li>
    056 * <li>Import pragma: whether the <code>#pragma jexl.import fully.qualified.class.name</code> syntax is allowed</li>
    057 * <li>Comparator names: whether the comparator operator names can be used (as in <code>gt</code> for &gt;,
    058 * <code>lt</code> for &lt;, ...)</li>
    059 * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
    060 * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
    061 * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
    062 * </ul>
    063 * @since 3.2
    064 */
    065public final class JexlFeatures {
    066    /** The false predicate. */
    067    public static final Predicate<String> TEST_STR_FALSE = s -> false;
    068    /** Te feature names (for toString()). */
    069    private static final String[] F_NAMES = {
    070        "register", "reserved variable", "local variable", "assign/modify",
    071        "global assign/modify", "array reference", "create instance", "loop", "function",
    072        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
    073        "thin-arrow", "fat-arrow", "namespace pragma", "import pragma", "comparator names", "pragma anywhere",
    074        "const capture"
    075    };
    076    /** Registers feature ordinal. */
    077    private static final int REGISTER = 0;
    078    /** Reserved future feature ordinal (unused as of 3.3.1). */
    079    public static final int RESERVED = 1;
    080    /** Locals feature ordinal. */
    081    public static final int LOCAL_VAR = 2;
    082    /** Side effects feature ordinal. */
    083    public static final int SIDE_EFFECT = 3;
    084    /** Global side effects feature ordinal. */
    085    public static final int SIDE_EFFECT_GLOBAL = 4;
    086    /** Expressions allowed in array reference ordinal. */
    087    public static final int ARRAY_REF_EXPR = 5;
    088    /** New-instance feature ordinal. */
    089    public static final int NEW_INSTANCE = 6;
    090    /** Loops feature ordinal. */
    091    public static final int LOOP = 7;
    092    /** Lambda feature ordinal. */
    093    public static final int LAMBDA = 8;
    094    /** Lambda feature ordinal. */
    095    public static final int METHOD_CALL = 9;
    096    /** Structured literal feature ordinal. */
    097    public static final int STRUCTURED_LITERAL = 10;
    098    /** Pragma feature ordinal. */
    099    public static final int PRAGMA = 11;
    100    /** Annotation feature ordinal. */
    101    public static final int ANNOTATION = 12;
    102    /** Script feature ordinal. */
    103    public static final int SCRIPT = 13;
    104    /** Lexical feature ordinal. */
    105    public static final int LEXICAL = 14;
    106    /** Lexical shade feature ordinal. */
    107    public static final int LEXICAL_SHADE = 15;
    108    /** Thin-arrow lambda syntax. */
    109    public static final int THIN_ARROW = 16;
    110    /** Fat-arrow lambda syntax. */
    111    public static final int FAT_ARROW = 17;
    112    /** Namespace pragma feature ordinal. */
    113    public static final int NS_PRAGMA = 18;
    114    /** Import pragma feature ordinal. */
    115    public static final int IMPORT_PRAGMA = 19;
    116    /** Comparator names (legacy) syntax. */
    117    public static final int COMPARATOR_NAMES = 20;
    118    /** The pragma anywhere feature ordinal. */
    119    public static final int PRAGMA_ANYWHERE = 21;
    120    /** Captured variables are const. */
    121    public static final int CONST_CAPTURE = 22;
    122    /**
    123     * All features.
    124     * N.B. ensure this is updated if additional features are added.
    125     */
    126    private static final long ALL_FEATURES = (1L << CONST_CAPTURE + 1) - 1L; // MUST REMAIN PRIVATE
    127    /**
    128     * The default features flag mask.
    129     * <p>Meant for compatibility with scripts written before 3.3.1</p>
    130     */
    131    private static final long DEFAULT_FEATURES = // MUST REMAIN PRIVATE
    132        1L << LOCAL_VAR
    133        | 1L << SIDE_EFFECT
    134        | 1L << SIDE_EFFECT_GLOBAL
    135        | 1L << ARRAY_REF_EXPR
    136        | 1L << NEW_INSTANCE
    137        | 1L << LOOP
    138        | 1L << LAMBDA
    139        | 1L << METHOD_CALL
    140        | 1L << STRUCTURED_LITERAL
    141        | 1L << PRAGMA
    142        | 1L << ANNOTATION
    143        | 1L << SCRIPT
    144        | 1L << THIN_ARROW
    145        | 1L << NS_PRAGMA
    146        | 1L << IMPORT_PRAGMA
    147        | 1L << COMPARATOR_NAMES
    148        | 1L << PRAGMA_ANYWHERE;
    149    /**
    150     * The canonical scripting (since 3.3.1) features flag mask based on the original default.
    151     * <p>Adds lexical, lexical-shade and const-capture but removes comparator-names and pragma-anywhere</p>
    152     */
    153    private static final long SCRIPT_FEATURES = // MUST REMAIN PRIVATE
    154        ( DEFAULT_FEATURES
    155        | 1L << LEXICAL
    156        | 1L << LEXICAL_SHADE
    157        | 1L << CONST_CAPTURE ) // these parentheses are necessary :-)
    158        & ~(1L << COMPARATOR_NAMES)
    159        & ~(1L << PRAGMA_ANYWHERE);
    160
    161    /**
    162     * Protected future syntactic elements.
    163     * <p><em>throw, switch, case, default, class, instanceof, jexl, $jexl</em></p>
    164     * @since 3.3.1
    165     */
    166    private static final Set<String> RESERVED_WORDS =
    167        Collections.unmodifiableSet(
    168            new HashSet<>(Arrays.asList(
    169                "switch", "case", "default", "class", "jexl", "$jexl")));
    170
    171    /*
    172     * *WARNING*
    173     * Static fields may be inlined by the Java compiler, so their _values_ effectively form part of the external API.
    174     * Classes that reference them need to be recompiled to pick up new values.
    175     * This means that changes in value are not binary compatible.
    176     * Such fields must be private or problems may occur.
    177     */
    178
    179     /**
    180     * Creates an all features enabled set.
    181     * @return a new instance of all features set
    182     * @since 3.3.1
    183     */
    184    public static JexlFeatures createAll() {
    185        return new JexlFeatures(ALL_FEATURES, null, null);
    186    }
    187
    188    /**
    189     * Creates a default features set suitable for basic but complete scripting needs.
    190     * <p>Maximizes compatibility with older version scripts (before 3.3), new projects should
    191     * use {@link JexlFeatures#createScript()} or equivalent features as a base.</p>
    192     * <p>The following scripting features are enabled:</p>
    193     * <ul>
    194     *   <li>local variable, {@link JexlFeatures#supportsLocalVar()}</li>
    195     *   <li>side effect, {@link JexlFeatures#supportsSideEffect()}</li>
    196     *   <li>global side effect, {@link JexlFeatures#supportsSideEffectGlobal()}</li>
    197     *   <li>array reference expression, {@link JexlFeatures#supportsStructuredLiteral()}</li>
    198     *   <li>new instance, {@link JexlFeatures#supportsNewInstance()} </li>
    199     *   <li>loop, {@link JexlFeatures#supportsLoops()}</li>
    200     *   <li>lambda, {@link JexlFeatures#supportsLambda()}</li>
    201     *   <li>method call, {@link JexlFeatures#supportsMethodCall()}</li>
    202     *   <li>structured literal, {@link JexlFeatures#supportsStructuredLiteral()}</li>
    203     *   <li>pragma, {@link JexlFeatures#supportsPragma()}</li>
    204     *   <li>annotation, {@link JexlFeatures#supportsAnnotation()}</li>
    205     *   <li>script, {@link JexlFeatures#supportsScript()}</li>
    206     *   <li>comparator names,  {@link JexlFeatures#supportsComparatorNames()}</li>
    207     *   <li>namespace pragma,  {@link JexlFeatures#supportsNamespacePragma()}</li>
    208     *   <li>import pragma, {@link JexlFeatures#supportsImportPragma()}</li>
    209     *   <li>pragma anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
    210     * </ul>
    211     * @return a new instance of a default scripting features set
    212     * @since 3.3.1
    213     */
    214    public static JexlFeatures createDefault() {
    215        return new JexlFeatures(DEFAULT_FEATURES, null, null);
    216    }
    217
    218    /**
    219     * Creates an empty feature set.
    220     * <p>This is the strictest base-set since no feature is allowed, suitable as-is only
    221     * for the simplest expressions.</p>
    222     * @return a new instance of an empty features set
    223     * @since 3.3.1
    224     */
    225    public static JexlFeatures createNone() {
    226        return new JexlFeatures(0L, null, null);
    227    }
    228
    229    /**
    230     * The modern scripting features set.
    231     * <p>This is the recommended set for new projects.</p>
    232     * <p>All default features with the following differences:</p>
    233     * <ul>
    234     * <li><em>disable</em> pragma-anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
    235     * <li><em>disable</em> comparator-names, {@link JexlFeatures#supportsComparatorNames()}</li>
    236     * <li><em>enable</em> lexical, {@link JexlFeatures#isLexical()}</li>
    237     * <li><em>enable</em> lexical-shade, {@link JexlFeatures#isLexicalShade()} </li>
    238     * <li><em>enable</em> const-capture, {@link JexlFeatures#supportsConstCapture()}</li>
    239     * </ul>
    240     * <p>It also adds a set of reserved words to enable future unencumbered syntax evolution:
    241     * <em>try, catch, throw, finally, switch, case, default, class, instanceof</em>
    242     * </p>
    243     * @return a new instance of a modern scripting features set
    244     * @since 3.3.1
    245     */
    246    public static JexlFeatures createScript() {
    247        return new JexlFeatures(SCRIPT_FEATURES, RESERVED_WORDS, null);
    248    }
    249
    250    /**
    251     * The text corresponding to a feature code.
    252     * @param feature the feature number
    253     * @return the feature name
    254     */
    255    public static String stringify(final int feature) {
    256        return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
    257    }
    258
    259    /** The feature flags. */
    260    private long flags;
    261
    262    /** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */
    263    private Set<String> reservedNames;
    264
    265    /** The namespace names. */
    266    private Predicate<String> nameSpaces;
    267
    268    /**
    269     * Creates default instance, equivalent to the result of calling the preferred alternative
    270     * {@link JexlFeatures#createDefault()}
    271     */
    272    public JexlFeatures() {
    273        this(DEFAULT_FEATURES, null, null);
    274    }
    275
    276    /**
    277     * Copy constructor.
    278     * @param features the feature to copy from
    279     */
    280    public JexlFeatures(final JexlFeatures features) {
    281        this(features.flags, features.reservedNames, features.nameSpaces);
    282    }
    283
    284    /**
    285     * An all member constructor for derivation.
    286     * <p>Not respecting immutability or thread-safety constraints for this class constructor arguments will
    287     * likely result in unexpected behavior.</p>
    288     * @param f flag
    289     * @param r reserved variable names; must be an immutable Set or thread-safe (concurrent or synchronized set)
    290     * @param n namespace predicate; must be stateless or thread-safe
    291     */
    292    protected JexlFeatures(final long f, final Set<String> r, final Predicate<String> n) {
    293        this.flags = f;
    294        this.reservedNames = r == null? Collections.emptySet() : r;
    295        this.nameSpaces = n == null? TEST_STR_FALSE : n;
    296    }
    297
    298    /**
    299     * Sets whether annotation constructs are enabled.
    300     * <p>
    301     * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
    302     * will throw a parsing exception.
    303     * </p>
    304     * @param flag true to enable, false to disable
    305     * @return this features instance
    306     */
    307    public JexlFeatures annotation(final boolean flag) {
    308        setFeature(ANNOTATION, flag);
    309        return this;
    310    }
    311
    312    /**
    313     * Sets whether array references expressions are enabled.
    314     * <p>
    315     * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
    316     * will throw a parsing exception;
    317     * </p>
    318     * @param flag true to enable, false to disable
    319     * @return this features instance
    320     */
    321    public JexlFeatures arrayReferenceExpr(final boolean flag) {
    322        setFeature(ARRAY_REF_EXPR, flag);
    323        return this;
    324    }
    325
    326    /**
    327     * Sets whether the legacy comparison operator names syntax is enabled.
    328     * <p>
    329     * When disabled, comparison operators names (eq;ne;le;lt;ge;gt)
    330     * will be treated as plain identifiers.
    331     * </p>
    332     * @param flag true to enable, false to disable
    333     * @return this features instance
    334     * @since 3.3
    335     */
    336    public JexlFeatures comparatorNames(final boolean flag) {
    337        setFeature(COMPARATOR_NAMES, flag);
    338        return this;
    339    }
    340
    341    /**
    342     * Sets whether lambda captured-variables are const or not.
    343     * <p>
    344     * When disabled, lambda-captured variables are implicitly converted to read-write local variable (let),
    345     * when enabled, those are implicitly converted to read-only local variables (const).
    346     * </p>
    347     * @param flag true to enable, false to disable
    348     * @return this features instance
    349     */
    350    public JexlFeatures constCapture(final boolean flag) {
    351        setFeature(CONST_CAPTURE, flag);
    352        return this;
    353    }
    354
    355    @Override
    356    public boolean equals(final Object obj) {
    357        if (this == obj) {
    358            return true;
    359        }
    360        if (obj == null) {
    361            return false;
    362        }
    363        if (getClass() != obj.getClass()) {
    364            return false;
    365        }
    366        final JexlFeatures other = (JexlFeatures) obj;
    367        if (this.flags != other.flags) {
    368            return false;
    369        }
    370        if (this.nameSpaces != other.nameSpaces) {
    371            return false;
    372        }
    373        if (!Objects.equals(this.reservedNames, other.reservedNames)) {
    374            return false;
    375        }
    376        return true;
    377    }
    378
    379    /**
    380     * Sets whether fat-arrow lambda syntax is enabled.
    381     * <p>
    382     * When disabled, parsing a script/expression using syntactic fat-arrow (=&lt;)
    383     * will throw a parsing exception.
    384     * </p>
    385     * @param flag true to enable, false to disable
    386     * @return this features instance
    387     * @since 3.3
    388     */
    389    public JexlFeatures fatArrow(final boolean flag) {
    390        setFeature(FAT_ARROW, flag);
    391        return this;
    392    }
    393
    394    /**
    395     * Gets a feature flag value.
    396     * @param feature feature ordinal
    397     * @return true if on, false if off
    398     */
    399    private boolean getFeature(final int feature) {
    400        return (flags & 1L << feature) != 0L;
    401    }
    402
    403    /**
    404     * @return these features&quot;s flags
    405     */
    406    public long getFlags() {
    407        return flags;
    408    }
    409
    410    /**
    411     * @return the (unmodifiable) set of reserved names.
    412     */
    413    public Set<String> getReservedNames() {
    414        return reservedNames;
    415    }
    416
    417    @Override
    418    public int hashCode() { //CSOFF: MagicNumber
    419        int hash = 3;
    420        hash = 53 * hash + (int) (this.flags ^ this.flags >>> 32);
    421        hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
    422        return hash;
    423    }
    424
    425    /**
    426     * Sets whether import pragma constructs are enabled.
    427     * <p>
    428     * When disabled, parsing a script/expression using syntactic import pragma constructs
    429     * (#pragma jexl.import....) will throw a parsing exception.
    430     * </p>
    431     * @param flag true to enable, false to disable
    432     * @return this features instance
    433     * @since 3.3
    434     */
    435    public JexlFeatures importPragma(final boolean flag) {
    436        setFeature(IMPORT_PRAGMA, flag);
    437        return this;
    438    }
    439
    440    /** @return whether lexical scope feature is enabled */
    441    public boolean isLexical() {
    442        return getFeature(LEXICAL);
    443    }
    444
    445    /** @return whether lexical shade feature is enabled */
    446    public boolean isLexicalShade() {
    447        return getFeature(LEXICAL_SHADE);
    448    }
    449
    450    /**
    451     * Checks whether a name is reserved.
    452     * @param name the name to check
    453     * @return true if reserved, false otherwise
    454     */
    455    public boolean isReservedName(final String name) {
    456        return name != null && reservedNames.contains(name);
    457    }
    458
    459    /**
    460     * Sets whether lambda/function constructs are enabled.
    461     * <p>
    462     * When disabled, parsing a script/expression using syntactic lambda constructs (-&gt;,function)
    463     * will throw a parsing exception.
    464     * </p>
    465     * @param flag true to enable, false to disable
    466     * @return this features instance
    467     */
    468    public JexlFeatures lambda(final boolean flag) {
    469        setFeature(LAMBDA, flag);
    470        return this;
    471    }
    472
    473    /**
    474     * Sets whether syntactic lexical mode is enabled.
    475     *
    476     * @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping
    477     * @return this features instance
    478     */
    479    public JexlFeatures lexical(final boolean flag) {
    480        setFeature(LEXICAL, flag);
    481        if (!flag) {
    482            setFeature(LEXICAL_SHADE, false);
    483        }
    484        return this;
    485    }
    486
    487    /**
    488     * Sets whether syntactic lexical shade is enabled.
    489     *
    490     * @param flag true means syntactic lexical shade is in effect and implies lexical scope
    491     * @return this features instance
    492     */
    493    public JexlFeatures lexicalShade(final boolean flag) {
    494        setFeature(LEXICAL_SHADE, flag);
    495        if (flag) {
    496            setFeature(LEXICAL, true);
    497        }
    498        return this;
    499    }
    500
    501    /**
    502     * Sets whether local variables are enabled.
    503     * <p>
    504     * When disabled, parsing a script/expression using a local variable or parameter syntax
    505     * will throw a parsing exception.
    506     * </p>
    507     * @param flag true to enable, false to disable
    508     * @return this features instance
    509     */
    510    public JexlFeatures localVar(final boolean flag) {
    511        setFeature(LOCAL_VAR, flag);
    512        return this;
    513    }
    514
    515    /**
    516     * Sets whether looping constructs are enabled.
    517     * <p>
    518     * When disabled, parsing a script/expression using syntactic looping constructs (for,while)
    519     * will throw a parsing exception.
    520     * </p>
    521     * @param flag true to enable, false to disable
    522     * @return this features instance
    523     */
    524    public JexlFeatures loops(final boolean flag) {
    525        setFeature(LOOP, flag);
    526        return this;
    527    }
    528
    529    /**
    530     * Sets whether method calls expressions are enabled.
    531     * <p>
    532     * When disabled, parsing a script/expression using 'obj.method()'
    533     * will throw a parsing exception;
    534     * </p>
    535     * @param flag true to enable, false to disable
    536     * @return this features instance
    537     */
    538    public JexlFeatures methodCall(final boolean flag) {
    539        setFeature(METHOD_CALL, flag);
    540        return this;
    541    }
    542
    543    /**
    544     * Sets whether namespace pragma constructs are enabled.
    545     * <p>
    546     * When disabled, parsing a script/expression using syntactic namespace pragma constructs
    547     * (#pragma jexl.namespace....) will throw a parsing exception.
    548     * </p>
    549     * @param flag true to enable, false to disable
    550     * @return this features instance
    551     * @since 3.3
    552     */
    553    public JexlFeatures namespacePragma(final boolean flag) {
    554        setFeature(NS_PRAGMA, flag);
    555        return this;
    556    }
    557
    558    /**
    559     * @return the declared namespaces test.
    560     */
    561    public Predicate<String> namespaceTest() {
    562        return nameSpaces;
    563    }
    564
    565    /**
    566     * Sets a test to determine namespace declaration.
    567     * @param names the name predicate
    568     * @return this features instance
    569     */
    570    public JexlFeatures namespaceTest(final Predicate<String> names) {
    571        nameSpaces = names == null ? TEST_STR_FALSE : names;
    572        return this;
    573    }
    574
    575    /**
    576     * Sets whether creating new instances is enabled.
    577     * <p>
    578     * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
    579     * using a class as functor will fail at runtime.
    580     * </p>
    581     * @param flag true to enable, false to disable
    582     * @return this features instance
    583     */
    584    public JexlFeatures newInstance(final boolean flag) {
    585        setFeature(NEW_INSTANCE, flag);
    586        return this;
    587    }
    588
    589    /**
    590     * Sets whether pragma constructs are enabled.
    591     * <p>
    592     * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
    593     * will throw a parsing exception.
    594     * </p>
    595     * @param flag true to enable, false to disable
    596     * @return this features instance
    597     */
    598    public JexlFeatures pragma(final boolean flag) {
    599        setFeature(PRAGMA, flag);
    600        if (!flag) {
    601            setFeature(NS_PRAGMA, false);
    602            setFeature(IMPORT_PRAGMA, false);
    603        }
    604        return this;
    605    }
    606
    607    /**
    608     * Sets whether pragma constructs can appear anywhere in the code.
    609     *
    610     * @param flag true to enable, false to disable
    611     * @return this features instance
    612     * @since 3.3
    613     */
    614    public JexlFeatures pragmaAnywhere(final boolean flag) {
    615        setFeature(PRAGMA_ANYWHERE, flag);
    616        return this;
    617    }
    618
    619    /**
    620     * Sets whether register are enabled.
    621     * <p>
    622     * This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
    623     * </p>
    624     * <p>
    625     * When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
    626     * </p>
    627     * @param flag true to enable, false to disable
    628     * @return this features instance
    629     */
    630    public JexlFeatures register(final boolean flag) {
    631        setFeature(REGISTER, flag);
    632        return this;
    633    }
    634
    635    /**
    636     * Sets a collection of reserved r precluding those to be used as local variables or parameter r.
    637     * @param names the r to reserve
    638     * @return this features instance
    639     */
    640    public JexlFeatures reservedNames(final Collection<String> names) {
    641        if (names == null || names.isEmpty()) {
    642            reservedNames = Collections.emptySet();
    643        } else {
    644            reservedNames = Collections.unmodifiableSet(new TreeSet<>(names));
    645        }
    646        return this;
    647    }
    648
    649    /**
    650     * Sets whether scripts constructs are enabled.
    651     * <p>
    652     * When disabled, parsing a script using syntactic script constructs (statements, ...)
    653     * will throw a parsing exception.
    654     * </p>
    655     * @param flag true to enable, false to disable
    656     * @return this features instance
    657     */
    658    public JexlFeatures script(final boolean flag) {
    659        setFeature(SCRIPT, flag);
    660        return this;
    661    }
    662
    663    /**
    664     * Sets a feature flag.
    665     * @param feature the feature ordinal
    666     * @param flag    turn-on, turn off
    667     */
    668    private void setFeature(final int feature, final boolean flag) {
    669        if (flag) {
    670            flags |= 1L << feature;
    671        } else {
    672            flags &= ~(1L << feature);
    673        }
    674    }
    675
    676    /**
    677     * Sets whether side effect expressions are enabled.
    678     * <p>
    679     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    680     * or members will throw a parsing exception.
    681     * </p>
    682     * @param flag true to enable, false to disable
    683     * @return this features instance
    684     */
    685    public JexlFeatures sideEffect(final boolean flag) {
    686        setFeature(SIDE_EFFECT, flag);
    687        return this;
    688    }
    689
    690    /**
    691     * Sets whether side effect expressions on global variables (aka non-local) are enabled.
    692     * <p>
    693     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    694     * <em>including all potentially ant-ish variables</em> will throw a parsing exception.
    695     * </p>
    696     * @param flag true to enable, false to disable
    697     * @return this features instance
    698     */
    699    public JexlFeatures sideEffectGlobal(final boolean flag) {
    700        setFeature(SIDE_EFFECT_GLOBAL, flag);
    701        return this;
    702    }
    703
    704    /**
    705     * Sets whether array/map/set literal expressions are enabled.
    706     * <p>
    707     * When disabled, parsing a script/expression creating one of these literals
    708     * will throw a parsing exception;
    709     * </p>
    710     * @param flag true to enable, false to disable
    711     * @return this features instance
    712     */
    713    public JexlFeatures structuredLiteral(final boolean flag) {
    714        setFeature(STRUCTURED_LITERAL, flag);
    715        return this;
    716    }
    717
    718    /**
    719     * @return true if annotation are enabled, false otherwise
    720     */
    721    public boolean supportsAnnotation() {
    722        return getFeature(ANNOTATION);
    723    }
    724
    725    /**
    726     * @return true if array references can contain method call expressions, false otherwise
    727     */
    728    public boolean supportsArrayReferenceExpr() {
    729        return getFeature(ARRAY_REF_EXPR);
    730    }
    731
    732    /**
    733     * @return true if legacy comparison operator names syntax is enabled, false otherwise
    734     * @since 3.3
    735     */
    736    public boolean supportsComparatorNames() {
    737        return getFeature(COMPARATOR_NAMES);
    738    }
    739
    740    /**
    741     * @return true if lambda captured-variables are const, false otherwise
    742     */
    743    public boolean supportsConstCapture() {
    744        return getFeature(CONST_CAPTURE);
    745    }
    746
    747    /**
    748     *
    749     * @return true if expressions (aka not scripts) are enabled, false otherwise
    750     */
    751    public boolean supportsExpression() {
    752        return !getFeature(SCRIPT);
    753    }
    754
    755    /**
    756     * @return true if fat-arrow lambda syntax is enabled, false otherwise
    757     * @since 3.3
    758     */
    759    public boolean supportsFatArrow() {
    760        return getFeature(FAT_ARROW);
    761    }
    762
    763    /**
    764     * @return true if import pragma are enabled, false otherwise
    765     * @since 3.3
    766     */
    767    public boolean supportsImportPragma() {
    768        return getFeature(IMPORT_PRAGMA);
    769    }
    770
    771    /**
    772     * @return true if lambda are enabled, false otherwise
    773     */
    774    public boolean supportsLambda() {
    775        return getFeature(LAMBDA);
    776    }
    777
    778    /**
    779     * @return true if local variables syntax is enabled
    780     */
    781    public boolean supportsLocalVar() {
    782        return getFeature(LOCAL_VAR);
    783    }
    784
    785    /**
    786     * @return true if loops are enabled, false otherwise
    787     */
    788    public boolean supportsLoops() {
    789        return getFeature(LOOP);
    790    }
    791
    792    /**
    793     * @return true if array references can contain expressions, false otherwise
    794     */
    795    public boolean supportsMethodCall() {
    796        return getFeature(METHOD_CALL);
    797    }
    798    /**
    799     * @return true if namespace pragma are enabled, false otherwise
    800     * @since 3.3
    801     */
    802    public boolean supportsNamespacePragma() {
    803        return getFeature(NS_PRAGMA);
    804    }
    805
    806    /**
    807     * @return true if creating new instances is enabled, false otherwise
    808     */
    809    public boolean supportsNewInstance() {
    810        return getFeature(NEW_INSTANCE);
    811    }
    812
    813    /**
    814     * @return true if namespace pragma are enabled, false otherwise
    815     */
    816    public boolean supportsPragma() {
    817        return getFeature(PRAGMA);
    818    }
    819
    820    /**
    821     * @return true if pragma constructs can appear anywhere in the code, false otherwise
    822     * @since 3.3
    823     */
    824    public boolean supportsPragmaAnywhere() {
    825        return getFeature(PRAGMA_ANYWHERE);
    826    }
    827
    828    /**
    829     * @return true if register syntax is enabled
    830     */
    831    public boolean supportsRegister() {
    832        return getFeature(REGISTER);
    833    }
    834
    835    /**
    836     * @return true if scripts are enabled, false otherwise
    837     */
    838    public boolean supportsScript() {
    839        return getFeature(SCRIPT);
    840    }
    841
    842    /**
    843     * @return true if side effects are enabled, false otherwise
    844     */
    845    public boolean supportsSideEffect() {
    846        return getFeature(SIDE_EFFECT);
    847    }
    848
    849    /**
    850     * @return true if global variables can be assigned
    851     */
    852    public boolean supportsSideEffectGlobal() {
    853        return getFeature(SIDE_EFFECT_GLOBAL);
    854    }
    855
    856    /**
    857     * @return true if array/map/set literal expressions are supported, false otherwise
    858     */
    859    public boolean supportsStructuredLiteral() {
    860        return getFeature(STRUCTURED_LITERAL);
    861    }
    862
    863    /**
    864     * @return true if thin-arrow lambda syntax is enabled, false otherwise
    865     * @since 3.3
    866     */
    867    public boolean supportsThinArrow() {
    868        return getFeature(THIN_ARROW);
    869    }
    870
    871    /**
    872     * Sets whether thin-arrow lambda syntax is enabled.
    873     * <p>
    874     * When disabled, parsing a script/expression using syntactic thin-arrow (-&lt;)
    875     * will throw a parsing exception.
    876     * </p>
    877     * @param flag true to enable, false to disable
    878     * @return this features instance
    879     * @since 3.3
    880     */
    881    public JexlFeatures thinArrow(final boolean flag) {
    882        setFeature(THIN_ARROW, flag);
    883        return this;
    884    }
    885}