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}