001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.jexl3;
018
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashSet;
023import java.util.Objects;
024import java.util.Set;
025import java.util.TreeSet;
026import java.util.function.Predicate;
027
028/**
029 * A set of language feature options.
030 * <p>
031 * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
032 * subclass of JexlException.Parsing) when disabled.
033 * </p>
034 * <p>It is recommended to be explicit in choosing the features you need rather than rely on the default
035 * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} and {@link JexlFeatures#createAll()}
036 * are the recommended starting points to selectively enable or disable chosen features.</p>
037 * <ul>
038 * <li>Registers: register syntax (#number), used internally for {g,s}etProperty</li>
039 * <li>Reserved Names: a set of reserved variable names that cannot be used as local variable (or parameter) names</li>
040 * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)</li>
041 * <li>Lexical: lexical scope, prevents redefining local variables</li>
042 * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one</li>
043 * <li>Side Effect : assigning/modifying values on any variables or left-value</li>
044 * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.</li>
045 * <li>New Instance: creating an instance using new(...)</li>
046 * <li>Loops: loop constructs (while(true), for(...))</li>
047 * <li>Lambda: function definitions (()-&gt;{...}, function(...) ).</li>
048 * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
049 * - including namespace prefixes - available</li>
050 * <li>Structured literals: arrays, lists, maps, sets, ranges</li>
051 * <li>Pragma: pragma construct as in {@code #pragma x y}</li>
052 * <li>Annotation: @annotation statement;</li>
053 * <li>Thin-arrow: use the thin-arrow, ie {@code ->} for lambdas as in {@code x -> x + x}</li>
054 * <li>Fat-arrow: use the  fat-arrow, ie {@code =>} for lambdas as in {@code x => x + x}
055 * <li>Namespace pragma: whether the {@code #pragma jexl.namespace.ns namespace} syntax is allowed</li>
056 * <li>Namespace identifier: whether the {@code ns:fun(...)} parser treats the ns:fun as one identifier, no spaces allowed</li>
057 * <li>Import pragma: whether the {@code #pragma jexl.import fully.qualified.class.name} syntax is allowed</li>
058 * <li>Comparator names: whether the comparator operator names can be used (as in {@code gt} for &gt;,
059 * {@code lt} for &lt;, ...)</li>
060 * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
061 * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
062 * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li>
063 * <li>Reference Capture: whether variables captured by lambdas are pass-by-reference or pass-by-value.</li>
064 * </ul>
065 *
066 * @since 3.2
067 */
068public final class JexlFeatures {
069
070    /** The false predicate. */
071    public static final Predicate<String> TEST_STR_FALSE = s -> false;
072
073    /** Te feature names (for toString()). */
074    private static final String[] F_NAMES = {
075        "register", "reserved variable", "local variable", "assign/modify",
076        "global assign/modify", "array reference", "create instance", "loop", "function",
077        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
078        "thin-arrow", "fat-arrow", "namespace pragma", "namespace identifier", "import pragma", "comparator names", "pragma anywhere",
079        "const capture", "ref capture", "ambiguous statement", "ignore template prefix"
080    };
081
082    /** Registers feature ordinal. */
083    private static final int REGISTER = 0;
084
085    /** Reserved future feature ordinal (unused as of 3.3.1). */
086    public static final int RESERVED = 1;
087
088    /** Locals feature ordinal. */
089    public static final int LOCAL_VAR = 2;
090
091    /** Side effects feature ordinal. */
092    public static final int SIDE_EFFECT = 3;
093
094    /** Global side effects feature ordinal. */
095    public static final int SIDE_EFFECT_GLOBAL = 4;
096
097    /** Expressions allowed in array reference ordinal. */
098    public static final int ARRAY_REF_EXPR = 5;
099
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}