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