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