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 (()->{...}, 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 >,
59 * {@code lt} for <, ...)</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 (=<)
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"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 (->,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 (-<)
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 }