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 cannot 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}
52   * <li>Annotation: @annotation statement;
53   * <li>Thin-arrow: use the thin-arrow, ie {@code ->} for lambdas as in {@code x -> x + x}
54   * <li>Fat-arrow: use the  fat-arrow, ie {@code =>} for lambdas as in {@code x => x + x}
55   * <li>Namespace pragma: whether the {@code #pragma jexl.namespace.ns namespace} syntax is allowed</li>
56   * <li>Namespace identifier: whether the {@code ns:fun(...)} parser treats the ns:fun as one identifier, no spaces allowed</li>
57   * <li>Import pragma: whether the {@code #pragma jexl.import fully.qualified.class.name} syntax is allowed</li>
58   * <li>Comparator names: whether the comparator operator names can be used (as in {@code gt} for &gt;,
59   * {@code lt} for &lt;, ...)</li>
60   * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
61   * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
62   * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
63   * <li>Reference Capture: whether variables captured by lambdas are pass-by-reference or pass-by-value.</li>
64   * </ul>
65   * @since 3.2
66   */
67  public final class JexlFeatures {
68      /** The false predicate. */
69      public static final Predicate<String> TEST_STR_FALSE = s -> false;
70      /** Te feature names (for toString()). */
71      private static final String[] F_NAMES = {
72          "register", "reserved variable", "local variable", "assign/modify",
73          "global assign/modify", "array reference", "create instance", "loop", "function",
74          "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
75          "thin-arrow", "fat-arrow", "namespace pragma", "namespace identifier", "import pragma", "comparator names", "pragma anywhere",
76          "const capture", "ref capture"
77      };
78      /** Registers feature ordinal. */
79      private static final int REGISTER = 0;
80      /** Reserved future feature ordinal (unused as of 3.3.1). */
81      public static final int RESERVED = 1;
82      /** Locals feature ordinal. */
83      public static final int LOCAL_VAR = 2;
84      /** Side effects feature ordinal. */
85      public static final int SIDE_EFFECT = 3;
86      /** Global side effects feature ordinal. */
87      public static final int SIDE_EFFECT_GLOBAL = 4;
88      /** Expressions allowed in array reference ordinal. */
89      public static final int ARRAY_REF_EXPR = 5;
90      /** New-instance feature ordinal. */
91      public static final int NEW_INSTANCE = 6;
92      /** Loops feature ordinal. */
93      public static final int LOOP = 7;
94      /** Lambda feature ordinal. */
95      public static final int LAMBDA = 8;
96      /** Lambda feature ordinal. */
97      public static final int METHOD_CALL = 9;
98      /** Structured literal feature ordinal. */
99      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 }