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