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 }