001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jexl3; 018 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashSet; 023import java.util.Objects; 024import java.util.Set; 025import java.util.TreeSet; 026import java.util.function.Predicate; 027 028/** 029 * A set of language feature options. 030 * <p> 031 * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a 032 * subclass of JexlException.Parsing) when disabled. 033 * </p> 034 * <p>It is recommended to be explicit in choosing the features you need rather than rely on the default 035 * constructor: the 2 convenience methods {@link JexlFeatures#createNone()} and {@link JexlFeatures#createAll()} 036 * are the recommended starting points to selectively enable or disable chosen features.</p> 037 * <ul> 038 * <li>Registers: register syntax (#number), used internally for {g,s}etProperty</li> 039 * <li>Reserved Names: a set of reserved variable names that cannot be used as local variable (or parameter) names</li> 040 * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)</li> 041 * <li>Lexical: lexical scope, prevents redefining local variables</li> 042 * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one</li> 043 * <li>Side Effect : assigning/modifying values on any variables or left-value</li> 044 * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.</li> 045 * <li>New Instance: creating an instance using new(...)</li> 046 * <li>Loops: loop constructs (while(true), for(...))</li> 047 * <li>Lambda: function definitions (()->{...}, function(...) ).</li> 048 * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls 049 * - including namespace prefixes - available</li> 050 * <li>Structured literals: arrays, lists, maps, sets, ranges</li> 051 * <li>Pragma: pragma construct as in {@code #pragma x y}</li> 052 * <li>Annotation: @annotation statement;</li> 053 * <li>Thin-arrow: use the thin-arrow, ie {@code ->} for lambdas as in {@code x -> x + x}</li> 054 * <li>Fat-arrow: use the fat-arrow, ie {@code =>} for lambdas as in {@code x => x + x} 055 * <li>Namespace pragma: whether the {@code #pragma jexl.namespace.ns namespace} syntax is allowed</li> 056 * <li>Namespace identifier: whether the {@code ns:fun(...)} parser treats the ns:fun as one identifier, no spaces allowed</li> 057 * <li>Import pragma: whether the {@code #pragma jexl.import fully.qualified.class.name} syntax is allowed</li> 058 * <li>Comparator names: whether the comparator operator names can be used (as in {@code gt} for >, 059 * {@code lt} for <, ...)</li> 060 * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins, 061 * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li> 062 * <li>Const Capture: whether variables captured by lambdas are read-only (aka const, same as Java) or read-write.</li> 063 * <li>Reference Capture: whether variables captured by lambdas are pass-by-reference or pass-by-value.</li> 064 * </ul> 065 * 066 * @since 3.2 067 */ 068public final class JexlFeatures { 069 070 /** The false predicate. */ 071 public static final Predicate<String> TEST_STR_FALSE = s -> false; 072 073 /** Te feature names (for toString()). */ 074 private static final String[] F_NAMES = { 075 "register", "reserved variable", "local variable", "assign/modify", 076 "global assign/modify", "array reference", "create instance", "loop", "function", 077 "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade", 078 "thin-arrow", "fat-arrow", "namespace pragma", "namespace identifier", "import pragma", "comparator names", "pragma anywhere", 079 "const capture", "ref capture", "ambiguous statement" 080 }; 081 082 /** Registers feature ordinal. */ 083 private static final int REGISTER = 0; 084 085 /** Reserved future feature ordinal (unused as of 3.3.1). */ 086 public static final int RESERVED = 1; 087 088 /** Locals feature ordinal. */ 089 public static final int LOCAL_VAR = 2; 090 091 /** Side effects feature ordinal. */ 092 public static final int SIDE_EFFECT = 3; 093 094 /** Global side effects feature ordinal. */ 095 public static final int SIDE_EFFECT_GLOBAL = 4; 096 097 /** Expressions allowed in array reference ordinal. */ 098 public static final int ARRAY_REF_EXPR = 5; 099 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}