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