View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.HashSet;
23  import java.util.Objects;
24  import java.util.Set;
25  import java.util.TreeSet;
26  import java.util.function.Predicate;
27  
28  /**
29   * A set of language feature options.
30   * <p>
31   * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
32   * subclass of JexlException.Parsing) when disabled.
33   * </p>
34   * <p>It is recommended to be explicit in choosing the features you need rather than rely on the default
35   * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} and {@link JexlFeatures#createAll()}
36   * are the recommended starting points to selectively enable or disable chosen features.</p>
37   * <ul>
38   * <li>Registers: register syntax (#number), used internally for {g,s}etProperty
39   * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
40   * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
41   * <li>Lexical: lexical scope, prevents redefining local variables
42   * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
43   * <li>Side Effect : assigning/modifying values on any variables or left-value
44   * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
45   * <li>New Instance: creating an instance using new(...)
46   * <li>Loops: loop constructs (while(true), for(...))
47   * <li>Lambda: function definitions (()-&gt;{...}, function(...) ).
48   * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
49   * - including namespace prefixes - available
50   * <li>Structured literals: arrays, lists, maps, sets, ranges
51   * <li>Pragma: pragma construct as in <code>#pragma x y</code>
52   * <li>Annotation: @annotation statement;
53   * <li>Thin-arrow: use the thin-arrow, ie <code>-&gt;</code> for lambdas as in <code>x -&gt; x + x</code>
54   * <li>Fat-arrow: use the  fat-arrow, ie <code>=&gt;</code> for lambdas as in <code>x =&gt; x + x</code>
55   * <li>Namespace pragma: whether the <code>#pragma jexl.namespace.ns namespace</code> syntax is allowed</li>
56   * <li>Import pragma: whether the <code>#pragma jexl.import fully.qualified.class.name</code> syntax is allowed</li>
57   * <li>Comparator names: whether the comparator operator names can be used (as in <code>gt</code> for &gt;,
58   * <code>lt</code> for &lt;, ...)</li>
59   * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
60   * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
61   * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
62   * </ul>
63   * @since 3.2
64   */
65  public final class JexlFeatures {
66      /** The false predicate. */
67      public static final Predicate<String> TEST_STR_FALSE = s -> false;
68      /** Te feature names (for toString()). */
69      private static final String[] F_NAMES = {
70          "register", "reserved variable", "local variable", "assign/modify",
71          "global assign/modify", "array reference", "create instance", "loop", "function",
72          "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
73          "thin-arrow", "fat-arrow", "namespace pragma", "import pragma", "comparator names", "pragma anywhere",
74          "const capture"
75      };
76      /** Registers feature ordinal. */
77      private static final int REGISTER = 0;
78      /** Reserved future feature ordinal (unused as of 3.3.1). */
79      public static final int RESERVED = 1;
80      /** Locals feature ordinal. */
81      public static final int LOCAL_VAR = 2;
82      /** Side effects feature ordinal. */
83      public static final int SIDE_EFFECT = 3;
84      /** Global side effects feature ordinal. */
85      public static final int SIDE_EFFECT_GLOBAL = 4;
86      /** Expressions allowed in array reference ordinal. */
87      public static final int ARRAY_REF_EXPR = 5;
88      /** New-instance feature ordinal. */
89      public static final int NEW_INSTANCE = 6;
90      /** Loops feature ordinal. */
91      public static final int LOOP = 7;
92      /** Lambda feature ordinal. */
93      public static final int LAMBDA = 8;
94      /** Lambda feature ordinal. */
95      public static final int METHOD_CALL = 9;
96      /** Structured literal feature ordinal. */
97      public static final int STRUCTURED_LITERAL = 10;
98      /** Pragma feature ordinal. */
99      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 }