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