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