    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 *      https://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 cannot 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}
    052 * <li>Annotation: @annotation statement;
    053 * <li>Thin-arrow: use the thin-arrow, ie {@code ->} for lambdas as in {@code x -> x + x}
    054 * <li>Fat-arrow: use the  fat-arrow, ie {@code =>} for lambdas as in {@code x => x + x}
    055 * <li>Namespace pragma: whether the {@code #pragma jexl.namespace.ns namespace} syntax is allowed</li>
    056 * <li>Namespace identifier: whether the {@code ns:fun(...)} parser treats the ns:fun as one identifier, no spaces allowed</li>
    057 * <li>Import pragma: whether the {@code #pragma jexl.import fully.qualified.class.name} syntax is allowed</li>
    058 * <li>Comparator names: whether the comparator operator names can be used (as in {@code gt} for &gt;,
    059 * {@code lt} for &lt;, ...)</li>
    060 * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
    061 * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
    062 * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
    063 * <li>Reference Capture: whether variables captured by lambdas are pass-by-reference or pass-by-value.</li>
    064 * </ul>
    065 * @since 3.2
    066 */
    067public final class JexlFeatures {
    068    /** The false predicate. */
    069    public static final Predicate<String> TEST_STR_FALSE = s -> false;
    070    /** Te feature names (for toString()). */
    071    private static final String[] F_NAMES = {
    072        "register", "reserved variable", "local variable", "assign/modify",
    073        "global assign/modify", "array reference", "create instance", "loop", "function",
    074        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
    075        "thin-arrow", "fat-arrow", "namespace pragma", "namespace identifier", "import pragma", "comparator names", "pragma anywhere",
    076        "const capture", "ref capture", "ambiguous statement"
    077    };
    078    /** Registers feature ordinal. */
    079    private static final int REGISTER = 0;
    080    /** Reserved future feature ordinal (unused as of 3.3.1). */
    081    public static final int RESERVED = 1;
    082    /** Locals feature ordinal. */
    083    public static final int LOCAL_VAR = 2;
    084    /** Side effects feature ordinal. */
    085    public static final int SIDE_EFFECT = 3;
    086    /** Global side effects feature ordinal. */
    087    public static final int SIDE_EFFECT_GLOBAL = 4;
    088    /** Expressions allowed in array reference ordinal. */
    089    public static final int ARRAY_REF_EXPR = 5;
    090    /** New-instance feature ordinal. */
    091    public static final int NEW_INSTANCE = 6;
    092    /** Loops feature ordinal. */
    093    public static final int LOOP = 7;
    094    /** Lambda feature ordinal. */
    095    public static final int LAMBDA = 8;
    096    /** Lambda feature ordinal. */
    097    public static final int METHOD_CALL = 9;
    098    /** Structured literal feature ordinal. */
    099    public static final int STRUCTURED_LITERAL = 10;
    100    /** Pragma feature ordinal. */
    101    public static final int PRAGMA = 11;
    102    /** Annotation feature ordinal. */
    103    public static final int ANNOTATION = 12;
    104    /** Script feature ordinal. */
    105    public static final int SCRIPT = 13;
    106    /** Lexical feature ordinal. */
    107    public static final int LEXICAL = 14;
    108    /** Lexical shade feature ordinal. */
    109    public static final int LEXICAL_SHADE = 15;
    110    /** Thin-arrow lambda syntax. */
    111    public static final int THIN_ARROW = 16;
    112    /** Fat-arrow lambda syntax. */
    113    public static final int FAT_ARROW = 17;
    114    /** Namespace pragma feature ordinal. */
    115    public static final int NS_PRAGMA = 18;
    116    /** Namespace syntax as an identifier (no space). */
    117    public static final int NS_IDENTIFIER = 19;
    118    /** Import pragma feature ordinal. */
    119    public static final int IMPORT_PRAGMA = 20;
    120    /** Comparator names (legacy) syntax. */
    121    public static final int COMPARATOR_NAMES = 21;
    122    /** The pragma anywhere feature ordinal. */
    123    public static final int PRAGMA_ANYWHERE = 22;
    124    /** Captured variables are const. */
    125    public static final int CONST_CAPTURE = 23;
    126    /** Captured variables are reference. */
    127    public static final int REF_CAPTURE = 24;
    128    /** Ambiguous or strict statement allowed. */
    129    public static final int AMBIGUOUS_STATEMENT = 25;
    130    /** Bad naming, use AMBIGUOUS_STATEMENT.
    131     * @deprecated 3.6
    132     */
    133    @Deprecated
    134    public static final int STRICT_STATEMENT = 25;
    135    /**
    136     * All features.
    137     * Ensure this is updated if additional features are added.
    138     */
    139    private static final long ALL_FEATURES = (1L << AMBIGUOUS_STATEMENT + 1) - 1L; // MUST REMAIN PRIVATE
    140    /**
    141     * The default features flag mask.
    142     * <p>Meant for compatibility with scripts written before 3.3.1</p>
    143     */
    144    private static final long DEFAULT_FEATURES = // MUST REMAIN PRIVATE
    145        1L << LOCAL_VAR
    146        | 1L << SIDE_EFFECT
    147        | 1L << SIDE_EFFECT_GLOBAL
    148        | 1L << ARRAY_REF_EXPR
    149        | 1L << NEW_INSTANCE
    150        | 1L << LOOP
    151        | 1L << LAMBDA
    152        | 1L << METHOD_CALL
    153        | 1L << STRUCTURED_LITERAL
    154        | 1L << PRAGMA
    155        | 1L << ANNOTATION
    156        | 1L << SCRIPT
    157        | 1L << THIN_ARROW
    158        | 1L << NS_PRAGMA
    159        | 1L << IMPORT_PRAGMA
    160        | 1L << COMPARATOR_NAMES
    161        | 1L << PRAGMA_ANYWHERE;
    162    /**
    163     * The canonical scripting (since 3.3.1) features flag mask based on the original default.
    164     * <p>Adds lexical, lexical-shade and const-capture but removes comparator-names and pragma-anywhere</p>
    165     */
    166    private static final long SCRIPT_FEATURES = // MUST REMAIN PRIVATE
    167        (DEFAULT_FEATURES
    168        | 1L << LEXICAL
    169        | 1L << LEXICAL_SHADE
    170        | 1L << CONST_CAPTURE) // these parentheses are necessary :-)
    171        & ~(1L << COMPARATOR_NAMES)
    172        & ~(1L << PRAGMA_ANYWHERE);
    173
    174    /**
    175     * Protected future syntactic elements.
    176     * <p><em>class, jexl, $jexl</em></p>
    177     * @since 3.3.1
    178     */
    179    private static final Set<String> RESERVED_WORDS =
    180        Collections.unmodifiableSet(new HashSet<>(Arrays.asList("class", "jexl", "$jexl")));
    181
    182    /*
    183     * *WARNING*
    184     * Static fields may be inlined by the Java compiler, so their _values_ effectively form part of the external API.
    185     * Classes that reference them need to be recompiled to pick up new values.
    186     * This means that changes in value are not binary compatible.
    187     * Such fields must be private or problems may occur.
    188     */
    189
    190     /**
    191     * Creates an all features enabled set.
    192     * @return a new instance of all features set
    193     * @since 3.3.1
    194     */
    195    public static JexlFeatures createAll() {
    196        return new JexlFeatures(ALL_FEATURES, null, null);
    197    }
    198
    199    /**
    200     * Creates a default features set suitable for basic but complete scripting needs.
    201     * <p>Maximizes compatibility with older version scripts (before 3.3), new projects should
    202     * use {@link JexlFeatures#createScript()} or equivalent features as a base.</p>
    203     * <p>The following scripting features are enabled:</p>
    204     * <ul>
    205     *   <li>local variable, {@link JexlFeatures#supportsLocalVar()}</li>
    206     *   <li>side effect, {@link JexlFeatures#supportsSideEffect()}</li>
    207     *   <li>global side effect, {@link JexlFeatures#supportsSideEffectGlobal()}</li>
    208     *   <li>array reference expression, {@link JexlFeatures#supportsStructuredLiteral()}</li>
    209     *   <li>new instance, {@link JexlFeatures#supportsNewInstance()} </li>
    210     *   <li>loop, {@link JexlFeatures#supportsLoops()}</li>
    211     *   <li>lambda, {@link JexlFeatures#supportsLambda()}</li>
    212     *   <li>method call, {@link JexlFeatures#supportsMethodCall()}</li>
    213     *   <li>structured literal, {@link JexlFeatures#supportsStructuredLiteral()}</li>
    214     *   <li>pragma, {@link JexlFeatures#supportsPragma()}</li>
    215     *   <li>annotation, {@link JexlFeatures#supportsAnnotation()}</li>
    216     *   <li>script, {@link JexlFeatures#supportsScript()}</li>
    217     *   <li>comparator names,  {@link JexlFeatures#supportsComparatorNames()}</li>
    218     *   <li>namespace pragma,  {@link JexlFeatures#supportsNamespacePragma()}</li>
    219     *   <li>import pragma, {@link JexlFeatures#supportsImportPragma()}</li>
    220     *   <li>pragma anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
    221     * </ul>
    222     * @return a new instance of a default scripting features set
    223     * @since 3.3.1
    224     */
    225    public static JexlFeatures createDefault() {
    226        return new JexlFeatures(DEFAULT_FEATURES, null, null);
    227    }
    228
    229    /**
    230     * Creates an empty feature set.
    231     * <p>This is the strictest base-set since no feature is allowed, suitable as-is only
    232     * for the simplest expressions.</p>
    233     * @return a new instance of an empty features set
    234     * @since 3.3.1
    235     */
    236    public static JexlFeatures createNone() {
    237        return new JexlFeatures(0L, null, null);
    238    }
    239
    240    /**
    241     * The modern scripting features set.
    242     * <p>This is the recommended set for new projects.</p>
    243     * <p>All default features with the following differences:</p>
    244     * <ul>
    245     * <li><em>disable</em> pragma-anywhere, {@link JexlFeatures#supportsPragmaAnywhere()}</li>
    246     * <li><em>disable</em> comparator-names, {@link JexlFeatures#supportsComparatorNames()}</li>
    247     * <li><em>enable</em> lexical, {@link JexlFeatures#isLexical()}</li>
    248     * <li><em>enable</em> lexical-shade, {@link JexlFeatures#isLexicalShade()} </li>
    249     * <li><em>enable</em> const-capture, {@link JexlFeatures#supportsConstCapture()}</li>
    250     * </ul>
    251     * <p>It also adds a set of reserved words to enable future unencumbered syntax evolution:
    252     * <em>try, catch, throw, finally, switch, case, default, class, instanceof</em>
    253     * </p>
    254     * @return a new instance of a modern scripting features set
    255     * @since 3.3.1
    256     */
    257    public static JexlFeatures createScript() {
    258        return new JexlFeatures(SCRIPT_FEATURES, RESERVED_WORDS, null);
    259    }
    260
    261    /**
    262     * The text corresponding to a feature code.
    263     * @param feature the feature number
    264     * @return the feature name
    265     */
    266    public static String stringify(final int feature) {
    267        return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
    268    }
    269
    270    /** The feature flags. */
    271    private long flags;
    272
    273    /** The set of reserved names, aka global variables that cannot be masked by local variables or parameters. */
    274    private Set<String> reservedNames;
    275
    276    /** The namespace names. */
    277    private Predicate<String> nameSpaces;
    278
    279    /**
    280     * Creates default instance, equivalent to the result of calling the preferred alternative
    281     * {@link JexlFeatures#createDefault()}
    282     */
    283    public JexlFeatures() {
    284        this(DEFAULT_FEATURES, null, null);
    285    }
    286
    287    /**
    288     * Copy constructor.
    289     * @param features the feature to copy from
    290     */
    291    public JexlFeatures(final JexlFeatures features) {
    292        this(features.flags, features.reservedNames, features.nameSpaces);
    293    }
    294
    295    /**
    296     * An all member constructor for derivation.
    297     * <p>Not respecting immutability or thread-safety constraints for this class constructor arguments will
    298     * likely result in unexpected behavior.</p>
    299     * @param f flag
    300     * @param r reserved variable names; must be an immutable Set or thread-safe (concurrent or synchronized set)
    301     * @param n namespace predicate; must be stateless or thread-safe
    302     */
    303    protected JexlFeatures(final long f, final Set<String> r, final Predicate<String> n) {
    304        this.flags = f;
    305        this.reservedNames = r == null? Collections.emptySet() : r;
    306        this.nameSpaces = n == null? TEST_STR_FALSE : n;
    307    }
    308
    309    /**
    310     * Sets whether annotation constructs are enabled.
    311     * <p>
    312     * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
    313     * will throw a parsing exception.
    314     * </p>
    315     * @param flag true to enable, false to disable
    316     * @return this features instance
    317     */
    318    public JexlFeatures annotation(final boolean flag) {
    319        setFeature(ANNOTATION, flag);
    320        return this;
    321    }
    322
    323    /**
    324     * Sets whether array references expressions are enabled.
    325     * <p>
    326     * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
    327     * will throw a parsing exception;
    328     * </p>
    329     * @param flag true to enable, false to disable
    330     * @return this features instance
    331     */
    332    public JexlFeatures arrayReferenceExpr(final boolean flag) {
    333        setFeature(ARRAY_REF_EXPR, flag);
    334        return this;
    335    }
    336
    337    /**
    338     * Sets whether the legacy comparison operator names syntax is enabled.
    339     * <p>
    340     * When disabled, comparison operators names (eq;ne;le;lt;ge;gt)
    341     * will be treated as plain identifiers.
    342     * </p>
    343     * @param flag true to enable, false to disable
    344     * @return this features instance
    345     * @since 3.3
    346     */
    347    public JexlFeatures comparatorNames(final boolean flag) {
    348        setFeature(COMPARATOR_NAMES, flag);
    349        return this;
    350    }
    351
    352    /**
    353     * Sets whether lambda captured-variables are constant or mutable.
    354     * <p>
    355     * When disabled, lambda-captured variables are implicitly converted to read-write local variable (let),
    356     * when enabled, those are implicitly converted to read-only local variables (const).
    357     * </p>
    358     * @param flag true to enable, false to disable
    359     * @return this features instance
    360     */
    361    public JexlFeatures constCapture(final boolean flag) {
    362        setFeature(CONST_CAPTURE, flag);
    363        return this;
    364    }
    365
    366    /**
    367     * Sets whether lambda captured-variables are references or values.
    368     * <p>When variables are pass-by-reference, side effects are visible from inner lexical scopes
    369     * to outer-scope.</p>
    370     * <p>
    371     * When disabled, lambda-captured variables use pass-by-value semantic,
    372     * when enabled, those use pass-by-reference semantic.
    373     * </p>
    374     * @param flag true to enable, false to disable
    375     * @return this features instance
    376     */
    377    public JexlFeatures referenceCapture(final boolean flag) {
    378        setFeature(REF_CAPTURE, flag);
    379        return this;
    380    }
    381
    382    @Override
    383    public boolean equals(final Object obj) {
    384        if (this == obj) {
    385            return true;
    386        }
    387        if (obj == null) {
    388            return false;
    389        }
    390        if (getClass() != obj.getClass()) {
    391            return false;
    392        }
    393        final JexlFeatures other = (JexlFeatures) obj;
    394        if (this.flags != other.flags) {
    395            return false;
    396        }
    397        if (this.nameSpaces != other.nameSpaces) {
    398            return false;
    399        }
    400        if (!Objects.equals(this.reservedNames, other.reservedNames)) {
    401            return false;
    402        }
    403        return true;
    404    }
    405
    406    /**
    407     * Sets whether fat-arrow lambda syntax is enabled.
    408     * <p>
    409     * When disabled, parsing a script/expression using syntactic fat-arrow (=&lt;)
    410     * will throw a parsing exception.
    411     * </p>
    412     * @param flag true to enable, false to disable
    413     * @return this features instance
    414     * @since 3.3
    415     */
    416    public JexlFeatures fatArrow(final boolean flag) {
    417        setFeature(FAT_ARROW, flag);
    418        return this;
    419    }
    420
    421    /**
    422     * Gets a feature flag value.
    423     * @param feature feature ordinal
    424     * @return true if on, false if off
    425     */
    426    private boolean getFeature(final int feature) {
    427        return (flags & 1L << feature) != 0L;
    428    }
    429
    430    /**
    431     * Gets the feature flags
    432     * @return these features&quot;s flags
    433     */
    434    public long getFlags() {
    435        return flags;
    436    }
    437
    438    /**
    439     * Gets the immutable set of reserved names.
    440     * @return the (unmodifiable) set of reserved names.
    441     */
    442    public Set<String> getReservedNames() {
    443        return reservedNames;
    444    }
    445
    446    @Override
    447    public int hashCode() { //CSOFF: MagicNumber
    448        int hash = 3;
    449        hash = 53 * hash + (int) (this.flags ^ this.flags >>> 32);
    450        hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
    451        return hash;
    452    }
    453
    454    /**
    455     * Sets whether import pragma constructs are enabled.
    456     * <p>
    457     * When disabled, parsing a script/expression using syntactic import pragma constructs
    458     * (#pragma jexl.import....) will throw a parsing exception.
    459     * </p>
    460     * @param flag true to enable, false to disable
    461     * @return this features instance
    462     * @since 3.3
    463     */
    464    public JexlFeatures importPragma(final boolean flag) {
    465        setFeature(IMPORT_PRAGMA, flag);
    466        return this;
    467    }
    468
    469    /**
    470     * Is the lexical scope feature enabled?
    471     * @return whether lexical scope feature is enabled */
    472    public boolean isLexical() {
    473        return getFeature(LEXICAL);
    474    }
    475
    476    /**
    477     * Is the lexical shade feature enabled?
    478     * @return whether lexical shade feature is enabled */
    479    public boolean isLexicalShade() {
    480        return getFeature(LEXICAL_SHADE);
    481    }
    482
    483    /**
    484     * Checks whether a name is reserved.
    485     * @param name the name to check
    486     * @return true if reserved, false otherwise
    487     */
    488    public boolean isReservedName(final String name) {
    489        return name != null && reservedNames.contains(name);
    490    }
    491
    492    /**
    493     * Sets whether lambda/function constructs are enabled.
    494     * <p>
    495     * When disabled, parsing a script/expression using syntactic lambda constructs (-&gt;,function)
    496     * will throw a parsing exception.
    497     * </p>
    498     * @param flag true to enable, false to disable
    499     * @return this features instance
    500     */
    501    public JexlFeatures lambda(final boolean flag) {
    502        setFeature(LAMBDA, flag);
    503        return this;
    504    }
    505
    506    /**
    507     * Sets whether syntactic lexical mode is enabled.
    508     *
    509     * @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping
    510     * @return this features instance
    511     */
    512    public JexlFeatures lexical(final boolean flag) {
    513        setFeature(LEXICAL, flag);
    514        if (!flag) {
    515            setFeature(LEXICAL_SHADE, false);
    516        }
    517        return this;
    518    }
    519
    520    /**
    521     * Sets whether syntactic lexical shade is enabled.
    522     *
    523     * @param flag true means syntactic lexical shade is in effect and implies lexical scope
    524     * @return this features instance
    525     */
    526    public JexlFeatures lexicalShade(final boolean flag) {
    527        setFeature(LEXICAL_SHADE, flag);
    528        if (flag) {
    529            setFeature(LEXICAL, true);
    530        }
    531        return this;
    532    }
    533
    534    /**
    535     * Sets whether local variables are enabled.
    536     * <p>
    537     * When disabled, parsing a script/expression using a local variable or parameter syntax
    538     * will throw a parsing exception.
    539     * </p>
    540     * @param flag true to enable, false to disable
    541     * @return this features instance
    542     */
    543    public JexlFeatures localVar(final boolean flag) {
    544        setFeature(LOCAL_VAR, flag);
    545        return this;
    546    }
    547
    548    /**
    549     * Sets whether looping constructs are enabled.
    550     * <p>
    551     * When disabled, parsing a script/expression using syntactic looping constructs (for,while)
    552     * will throw a parsing exception.
    553     * </p>
    554     * @param flag true to enable, false to disable
    555     * @return this features instance
    556     */
    557    public JexlFeatures loops(final boolean flag) {
    558        setFeature(LOOP, flag);
    559        return this;
    560    }
    561
    562    /**
    563     * Sets whether method calls expressions are enabled.
    564     * <p>
    565     * When disabled, parsing a script/expression using 'obj.method()'
    566     * will throw a parsing exception;
    567     * </p>
    568     * @param flag true to enable, false to disable
    569     * @return this features instance
    570     */
    571    public JexlFeatures methodCall(final boolean flag) {
    572        setFeature(METHOD_CALL, flag);
    573        return this;
    574    }
    575
    576    /**
    577     * Sets whether namespace pragma constructs are enabled.
    578     * <p>
    579     * When disabled, parsing a script/expression using syntactic namespace pragma constructs
    580     * (#pragma jexl.namespace....) will throw a parsing exception.
    581     * </p>
    582     * @param flag true to enable, false to disable
    583     * @return this features instance
    584     * @since 3.3
    585     */
    586    public JexlFeatures namespacePragma(final boolean flag) {
    587        setFeature(NS_PRAGMA, flag);
    588        return this;
    589    }
    590
    591    /**
    592     * Sets whether namespace as identifier syntax is enabled.
    593     * <p>
    594     * When enabled, a namespace call must be of the form <code>ns:fun(...)</code> with no
    595     * spaces between the namespace name and the function.
    596     * </p>
    597     * @param flag true to enable, false to disable
    598     * @return this features instance
    599     * @since 3.5.0
    600     */
    601    public JexlFeatures namespaceIdentifier(final boolean flag) {
    602        setFeature(NS_IDENTIFIER, flag);
    603        return this;
    604    }
    605
    606    /**
    607     * Gets the declared namespaces test.
    608     * @return the declared namespaces test.
    609     */
    610    public Predicate<String> namespaceTest() {
    611        return nameSpaces;
    612    }
    613
    614    /**
    615     * Sets a test to determine namespace declaration.
    616     * @param names the name predicate
    617     * @return this features instance
    618     */
    619    public JexlFeatures namespaceTest(final Predicate<String> names) {
    620        nameSpaces = names == null ? TEST_STR_FALSE : names;
    621        return this;
    622    }
    623
    624    /**
    625     * Sets whether creating new instances is enabled.
    626     * <p>
    627     * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
    628     * using a class as functor will fail at runtime.
    629     * </p>
    630     * @param flag true to enable, false to disable
    631     * @return this features instance
    632     */
    633    public JexlFeatures newInstance(final boolean flag) {
    634        setFeature(NEW_INSTANCE, flag);
    635        return this;
    636    }
    637
    638    /**
    639     * Sets whether pragma constructs are enabled.
    640     * <p>
    641     * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
    642     * will throw a parsing exception.
    643     * </p>
    644     * @param flag true to enable, false to disable
    645     * @return this features instance
    646     */
    647    public JexlFeatures pragma(final boolean flag) {
    648        setFeature(PRAGMA, flag);
    649        if (!flag) {
    650            setFeature(NS_PRAGMA, false);
    651            setFeature(IMPORT_PRAGMA, false);
    652        }
    653        return this;
    654    }
    655
    656    /**
    657     * Sets whether pragma constructs can appear anywhere in the code.
    658     *
    659     * @param flag true to enable, false to disable
    660     * @return this features instance
    661     * @since 3.3
    662     */
    663    public JexlFeatures pragmaAnywhere(final boolean flag) {
    664        setFeature(PRAGMA_ANYWHERE, flag);
    665        return this;
    666    }
    667
    668    /**
    669     * Sets whether register are enabled.
    670     * <p>
    671     * This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
    672     * </p>
    673     * <p>
    674     * When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
    675     * </p>
    676     * @param flag true to enable, false to disable
    677     * @return this features instance
    678     */
    679    public JexlFeatures register(final boolean flag) {
    680        setFeature(REGISTER, flag);
    681        return this;
    682    }
    683
    684    /**
    685     * Sets a collection of reserved r precluding those to be used as local variables or parameter r.
    686     * @param names the r to reserve
    687     * @return this features instance
    688     */
    689    public JexlFeatures reservedNames(final Collection<String> names) {
    690        if (names == null || names.isEmpty()) {
    691            reservedNames = Collections.emptySet();
    692        } else {
    693            reservedNames = Collections.unmodifiableSet(new TreeSet<>(names));
    694        }
    695        return this;
    696    }
    697
    698    /**
    699     * Sets whether scripts constructs are enabled.
    700     * <p>
    701     * When disabled, parsing a script using syntactic script constructs (statements, ...)
    702     * will throw a parsing exception.
    703     * </p>
    704     * @param flag true to enable, false to disable
    705     * @return this features instance
    706     */
    707    public JexlFeatures script(final boolean flag) {
    708        setFeature(SCRIPT, flag);
    709        return this;
    710    }
    711
    712    /**
    713     * Sets a feature flag.
    714     * @param feature the feature ordinal
    715     * @param flag    turn-on, turn off
    716     */
    717    private void setFeature(final int feature, final boolean flag) {
    718        if (flag) {
    719            flags |= 1L << feature;
    720        } else {
    721            flags &= ~(1L << feature);
    722        }
    723    }
    724
    725    /**
    726     * Sets whether statements can be ambiguous.
    727     * <p>
    728     * When enabled, the semicolumn is not required between expressions that otherwise are considered
    729     * ambiguous. The default will report ambiguity in cases like <code>if (true) { x 5 }</code> considering this
    730     * may be missing an operator or that the intent is not clear.
    731     * </p>
    732     * @param flag true to enable, false to disable
    733     * @return this features instance
    734     */
    735    public JexlFeatures ambiguousStatement(final boolean flag) {
    736        setFeature(AMBIGUOUS_STATEMENT, flag);
    737        return this;
    738    }
    739
    740    /**
    741     * Checks whether statements can be ambiguous.
    742     * <p>
    743     * When enabled, the semicolumn is not required between expressions that otherwise are considered
    744     * ambiguous. The default will report ambiguity in cases like <code>if (true) { x 5 }</code> considering this
    745     * may be missing an operator or that the intent is not clear.
    746     * </p>
    747     * @return true if statements can be ambiguous, false otherwise
    748     */
    749    public boolean supportsAmbiguousStatement() {
    750        return getFeature(AMBIGUOUS_STATEMENT);
    751    }
    752
    753    /**
    754     * Sets whether side effect expressions are enabled.
    755     * <p>
    756     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    757     * or members will throw a parsing exception.
    758     * </p>
    759     * @param flag true to enable, false to disable
    760     * @return this features instance
    761     */
    762    public JexlFeatures sideEffect(final boolean flag) {
    763        setFeature(SIDE_EFFECT, flag);
    764        return this;
    765    }
    766
    767    /**
    768     * Sets whether side effect expressions on global variables (aka non-local) are enabled.
    769     * <p>
    770     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    771     * <em>including all potentially ant-ish variables</em> will throw a parsing exception.
    772     * </p>
    773     * @param flag true to enable, false to disable
    774     * @return this features instance
    775     */
    776    public JexlFeatures sideEffectGlobal(final boolean flag) {
    777        setFeature(SIDE_EFFECT_GLOBAL, flag);
    778        return this;
    779    }
    780
    781    /**
    782     * Sets whether array/map/set literal expressions are enabled.
    783     * <p>
    784     * When disabled, parsing a script/expression creating one of these literals
    785     * will throw a parsing exception;
    786     * </p>
    787     * @param flag true to enable, false to disable
    788     * @return this features instance
    789     */
    790    public JexlFeatures structuredLiteral(final boolean flag) {
    791        setFeature(STRUCTURED_LITERAL, flag);
    792        return this;
    793    }
    794
    795    /**
    796     * Does the engine support annotations?
    797     * @return true if annotation are enabled, false otherwise
    798     */
    799    public boolean supportsAnnotation() {
    800        return getFeature(ANNOTATION);
    801    }
    802
    803    /**
    804     * Does the engine support array references which contain method call expressions?
    805     * @return true if array references can contain method call expressions, false otherwise
    806     */
    807    public boolean supportsArrayReferenceExpr() {
    808        return getFeature(ARRAY_REF_EXPR);
    809    }
    810
    811    /**
    812     * Does the engine support legacy comparison operator names syntax?
    813     * @return true if legacy comparison operator names syntax is enabled, false otherwise
    814     * @since 3.3
    815     */
    816    public boolean supportsComparatorNames() {
    817        return getFeature(COMPARATOR_NAMES);
    818    }
    819
    820    /**
    821     * Does the engine support lambda captured-variables as const?
    822     * @return true if lambda captured-variables are const, false otherwise
    823     */
    824    public boolean supportsConstCapture() {
    825        return getFeature(CONST_CAPTURE);
    826    }
    827
    828    /**
    829     * Does the engine support lambda captured-variables as references?
    830     * @return true if lambda captured-variables are references, false otherwise
    831     */
    832    public boolean supportsReferenceCapture() {
    833        return getFeature(REF_CAPTURE);
    834    }
    835
    836    /**
    837     * Does the engine support expressions (aka not scripts)
    838     * @return true if expressions (aka not scripts) are enabled, false otherwise
    839     */
    840    public boolean supportsExpression() {
    841        return !getFeature(SCRIPT);
    842    }
    843
    844    /**
    845     * Does the engine support fat-arrow lambda syntax?
    846     * @return true if fat-arrow lambda syntax is enabled, false otherwise
    847     * @since 3.3
    848     */
    849    public boolean supportsFatArrow() {
    850        return getFeature(FAT_ARROW);
    851    }
    852
    853    /**
    854     * Does the engine support import pragma?
    855     * @return true if import pragma are enabled, false otherwise
    856     * @since 3.3
    857     */
    858    public boolean supportsImportPragma() {
    859        return getFeature(IMPORT_PRAGMA);
    860    }
    861
    862    /**
    863     * Does the engine support lambdas?
    864     * @return true if lambda are enabled, false otherwise
    865     */
    866    public boolean supportsLambda() {
    867        return getFeature(LAMBDA);
    868    }
    869
    870    /**
    871     * Is local variables syntax enabled?
    872     * @return true if local variables syntax is enabled
    873     */
    874    public boolean supportsLocalVar() {
    875        return getFeature(LOCAL_VAR);
    876    }
    877
    878    /**
    879     * Are loops enabled?
    880     * @return true if loops are enabled, false otherwise
    881     */
    882    public boolean supportsLoops() {
    883        return getFeature(LOOP);
    884    }
    885
    886    /**
    887     * Can array references contain expressions?
    888     * @return true if array references can contain expressions, false otherwise
    889     */
    890    public boolean supportsMethodCall() {
    891        return getFeature(METHOD_CALL);
    892    }
    893
    894    /**
    895     * Is namespace pragma enabled?
    896     * @return true if namespace pragma are enabled, false otherwise
    897     * @since 3.3
    898     */
    899    public boolean supportsNamespacePragma() {
    900        return getFeature(NS_PRAGMA);
    901    }
    902
    903    /**
    904     * Is namespace identifier syntax enabled?
    905     * @return true if namespace identifier syntax is enabled, false otherwise
    906     * @since 3.5.0
    907     */
    908    public boolean supportsNamespaceIdentifier() {
    909        return getFeature(NS_IDENTIFIER);
    910    }
    911
    912    /**
    913     * Is creating new instances enabled?
    914     * @return true if creating new instances is enabled, false otherwise
    915     */
    916    public boolean supportsNewInstance() {
    917        return getFeature(NEW_INSTANCE);
    918    }
    919
    920    /**
    921     * Is the namespace pragma enabled?
    922     * @return true if namespace pragma are enabled, false otherwise
    923     */
    924    public boolean supportsPragma() {
    925        return getFeature(PRAGMA);
    926    }
    927
    928    /**
    929     * Can pragma constructs appear anywhere in the code?
    930     * @return true if pragma constructs can appear anywhere in the code, false otherwise
    931     * @since 3.3
    932     */
    933    public boolean supportsPragmaAnywhere() {
    934        return getFeature(PRAGMA_ANYWHERE);
    935    }
    936
    937    /**
    938     * Is register syntax enabled?
    939     * @return true if register syntax is enabled
    940     */
    941    public boolean supportsRegister() {
    942        return getFeature(REGISTER);
    943    }
    944
    945    /**
    946     * Are scripts enabled?
    947     * @return true if scripts are enabled, false otherwise
    948     */
    949    public boolean supportsScript() {
    950        return getFeature(SCRIPT);
    951    }
    952
    953    /**
    954     * Are side effects enabled?
    955     * @return true if side effects are enabled, false otherwise
    956     */
    957    public boolean supportsSideEffect() {
    958        return getFeature(SIDE_EFFECT);
    959    }
    960
    961    /**
    962     * Can global variables be assigned?
    963     * @return true if global variables can be assigned
    964     */
    965    public boolean supportsSideEffectGlobal() {
    966        return getFeature(SIDE_EFFECT_GLOBAL);
    967    }
    968
    969    /**
    970     * Are array/map/set literal expressions supported?
    971     * @return true if array/map/set literal expressions are supported, false otherwise
    972     */
    973    public boolean supportsStructuredLiteral() {
    974        return getFeature(STRUCTURED_LITERAL);
    975    }
    976
    977    /**
    978     * Is thin-arrow lambda syntax enabled?
    979     * @return true if thin-arrow lambda syntax is enabled, false otherwise
    980     * @since 3.3
    981     */
    982    public boolean supportsThinArrow() {
    983        return getFeature(THIN_ARROW);
    984    }
    985
    986    /**
    987     * Sets whether thin-arrow lambda syntax is enabled.
    988     * <p>
    989     * When disabled, parsing a script/expression using syntactic thin-arrow (-&lt;)
    990     * will throw a parsing exception.
    991     * </p>
    992     * @param flag true to enable, false to disable
    993     * @return this features instance
    994     * @since 3.3
    995     */
    996    public JexlFeatures thinArrow(final boolean flag) {
    997        setFeature(THIN_ARROW, flag);
    998        return this;
    999    }
    1000}