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