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