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