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