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}