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