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