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