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 */ 017 018package org.apache.commons.jexl3; 019 020import java.nio.charset.Charset; 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.Map; 024import java.util.function.IntFunction; 025import java.util.function.Supplier; 026 027import org.apache.commons.jexl3.internal.Engine; 028import org.apache.commons.jexl3.internal.SoftCache; 029import org.apache.commons.jexl3.introspection.JexlPermissions; 030import org.apache.commons.jexl3.introspection.JexlSandbox; 031import org.apache.commons.jexl3.introspection.JexlUberspect; 032import org.apache.commons.jexl3.parser.JexlScriptParser; 033import org.apache.commons.logging.Log; 034 035/** 036 * Configures and builds a JexlEngine. 037 * 038 * <p> 039 * The builder allow fine-tuning an engine instance behavior according to various control needs. 040 * Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>. 041 * </p><p> 042 * Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL 043 * syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control 044 * the visible set of objects - for instance, avoiding access to any object in java.rmi.* -. 045 * </p><p> 046 * Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most 047 * common flags accessible from the builder are reflected in its options ({@link #options()}). 048 * </p><p> 049 * The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as 050 * warning, when false, they throw {@link JexlException} exceptions. 051 * </p><p> 052 * The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe} 053 * flog determines if safe-navigation is used. Safe-navigation allows an evaluation shortcut and return null in expressions 054 * that attempts dereferencing null, typically a method call or accessing a property. 055 * </p><p> 056 * The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for 057 * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be 058 * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be 059 * preferred since they will detect violations at parsing time. (see {@link JexlFeatures}) 060 * </p><p> 061 * The following rules apply on silent and strict flags: 062 * </p> 063 * <ul> 064 * <li>When "silent" & "not-strict": 065 * <p> 0 & null should be indicators of "default" values so that even in an case of error, 066 * something meaningful can still be inferred; may be convenient for configurations. 067 * </p> 068 * </li> 069 * <li>When "silent" & "strict": 070 * <p>One should probably consider using null as an error case - ie, every object 071 * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form 072 * can be used to workaround exceptional cases. 073 * Use case could be configuration with no implicit values or defaults. 074 * </p> 075 * </li> 076 * <li>When "not-silent" & "not-strict": 077 * <p>The error control grain is roughly on par with JEXL 1.0</p> 078 * </li> 079 * <li>When "not-silent" & "strict": 080 * <p>The finest error control grain is obtained; it is the closest to Java code - 081 * still augmented by "script" capabilities regarding automated conversions and type matching. 082 * </p> 083 * </li> 084 * </ul> 085 */ 086public class JexlBuilder { 087 088 /** 089 * The set of default permissions used when creating a new builder. 090 * <p>Static but modifiable, so these default permissions can be changed to a purposeful set.</p> 091 * <p>In JEXL 3.7, these are {@link JexlPermissions#SECURE}.</p> 092 * <p>In JEXL 3.3, these were {@link JexlPermissions#RESTRICTED}.</p> 093 * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p> 094 */ 095 private static JexlPermissions PERMISSIONS = JexlPermissions.SECURE; 096 097 /** 098 * The full feature set that was the implicit default before JEXL 3.7. 099 * <p>This captures {@link JexlFeatures#createDefault()} - the feature set the engine used as its 100 * fallback throughout 3.x (since 3.3) before the 3.7 hardening. It enables {@code new(...)}, loops, 101 * side-effects, lambdas, method calls, structured literals, pragmas, etc. Use it (or 102 * {@link #setDefaultFeatures(JexlFeatures)}) to restore pre-3.7 behavior:</p> 103 * <pre> 104 * JexlEngine jexl = new JexlBuilder().features(JexlBuilder.FULL).create(); 105 * </pre> 106 * <p>{@link JexlFeatures} instances are mutable; copy this one via {@code new JexlFeatures(JexlBuilder.FULL)} 107 * before customizing it rather than mutating the shared instance.</p> 108 * 109 * @since 3.7.0 110 */ 111 public static final JexlFeatures FULL = JexlFeatures.createDefault(); 112 113 /** 114 * The set of default features used when creating a new builder. 115 * <p>Static but modifiable via {@link #setDefaultFeatures(JexlFeatures)}.</p> 116 * <p>In JEXL 3.7, the default hardens the feature set relative to {@link #FULL}: {@code new(...)}, 117 * global side-effects, pragmas, and annotations are disabled, and lexical scoping (with shade) is 118 * enabled. Loops remain available to scripts (expressions never allow them). Scripts using a 119 * disabled construct will throw {@link JexlException.Feature} at parse time.</p> 120 * 121 * @see #secureFeatures() 122 */ 123 private static JexlFeatures FEATURES = secureFeatures(); 124 125 /** The default maximum expression length to hit the expression cache. */ 126 protected static final int CACHE_THRESHOLD = 64; 127 128 /** 129 * Sets the default permissions. 130 * 131 * @param permissions The permissions 132 */ 133 public static void setDefaultPermissions(final JexlPermissions permissions) { 134 PERMISSIONS = permissions == null ? JexlPermissions.SECURE : permissions; 135 } 136 137 /** 138 * Sets the default features used when creating a new builder. 139 * <p>Passing {@code null} restores the 3.7 hardened default (new-instance, global side-effects, 140 * pragmas, and annotations disabled, lexical scoping enabled; see {@link #secureFeatures()}).</p> 141 * 142 * @param features The features, or {@code null} to restore the hardened default 143 * @since 3.7.0 144 */ 145 public static void setDefaultFeatures(final JexlFeatures features) { 146 FEATURES = features == null ? secureFeatures() : features; 147 } 148 149 /** 150 * Sets the default option flags used when creating a new engine. 151 * <p>Delegates to {@link JexlOptions#setDefaultFlags(String...)}; see that method for the 152 * flag-name syntax ({@code "+flag"}/{@code "-flag"}) and the available flag names 153 * ({@code cancellable}, {@code strict}, {@code silent}, {@code safe}, {@code lexical}, 154 * {@code antish}, {@code lexicalShade}, ...).</p> 155 * <p>Example: restore safe/antish defaults before creating any engine:</p> 156 * <pre>JexlBuilder.setDefaultOptions("+safe", "+antish");</pre> 157 * 158 * @param flags The flags to set or clear 159 * @since 3.7.0 160 */ 161 public static void setDefaultOptions(final String... flags) { 162 JexlOptions.setDefaultFlags(flags); 163 } 164 165 /** 166 * Builds the JEXL 3.7 hardened default feature set. 167 * <p>Starting from {@link #FULL} (the pre-3.7 feature set), this disables {@code new(...)}, 168 * global side-effects, pragmas, annotations, namespace instantiation, and enables lexical scoping and lexical shade. 169 * Loops are left enabled so scripts can iterate; expressions never allow loops since they are 170 * parsed as a single expression.</p> 171 * 172 * @return A fresh hardened feature set 173 * @since 3.7.0 174 */ 175 private static JexlFeatures secureFeatures() { 176 return new JexlFeatures(FULL) 177 .newInstance(false) 178 .sideEffectGlobal(false) 179 .pragma(false) 180 .annotation(false) 181 .loops(true) 182 .lexical(true) 183 .lexicalShade(true) 184 .namespaceInstantiation(false); 185 } 186 187 /** The JexlUberspect instance. */ 188 private JexlUberspect uberspect; 189 190 /** The {@link JexlUberspect} resolver strategy. */ 191 private JexlUberspect.ResolverStrategy strategy; 192 193 /** The set of permissions. */ 194 private JexlPermissions permissions; 195 196 /** The sandbox. */ 197 private JexlSandbox sandbox; 198 199 /** The Log to which all JexlEngine messages will be logged. */ 200 private Log logger; 201 202 /** Whether error messages will carry debugging information. */ 203 private Boolean debug; 204 205 /** Whether interrupt throws JexlException.Cancel. */ 206 private Boolean cancellable; 207 208 /** The options. */ 209 private final JexlOptions options = new JexlOptions(); 210 211 /** Whether getVariables considers all potential equivalent syntactic forms. */ 212 private int collectMode = 1; 213 214 /** The {@link JexlArithmetic} instance. */ 215 private JexlArithmetic arithmetic; 216 217 /** The cache size. */ 218 private int cache = -1; 219 220 /** The cache class factory. */ 221 private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new; 222 223 /** The parser class factory. */ 224 private Supplier<JexlScriptParser> parserFactory; 225 226 /** The stack overflow limit. */ 227 private int stackOverflow = Integer.MAX_VALUE; 228 229 /** The maximum expression length to hit the expression cache. */ 230 private int cacheThreshold = CACHE_THRESHOLD; 231 232 /** The charset. */ 233 private Charset charset = Charset.defaultCharset(); 234 235 /** The class loader. */ 236 private ClassLoader loader; 237 238 /** The features. */ 239 private JexlFeatures features; 240 241 /** 242 * Default constructor. 243 * <p> 244 * As of JEXL 3.3, to reduce the security risks inherent to JEXL"s purpose, the builder will use a set of 245 * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes 246 * and methods are visible to JEXL and usable in scripts using default implicit behaviors. 247 * </p><p> 248 * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing 249 * your own class instances through arguments, contexts, or namespaces. 250 * The default set of allowed packages and denied classes is described by {@link JexlPermissions#SECURE} 251 * as of JEXL 3.7 (previously {@link JexlPermissions#RESTRICTED} since 3.3). 252 * </p><p> 253 * As of JEXL 3.7, the default features also disable {@code new(...)}, global side-effects, pragmas, 254 * and annotations, and enable lexical scoping; loops remain available to scripts (but never to 255 * expressions). Scripts using a disabled construct will throw {@link JexlException.Feature} at parse 256 * time unless a permissive feature set is configured. 257 * </p><p> 258 * The recommended mitigation if your usage of JEXL is impacted is to load a {@code jexl.yaml} 259 * configuration via {@link JexlConfigLoader} that restores the legacy permissions and features, 260 * or to explicitly configure them via {@link #permissions(JexlPermissions)} and 261 * {@link #features(JexlFeatures)}. Use {@link JexlPermissions#logging()} to discover which 262 * reflective elements the new algorithm denies. 263 * </p><p> 264 * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior 265 * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}. 266 * </p> 267 * 268 * @since 3.3 269 */ 270 public JexlBuilder() { 271 this.permissions = PERMISSIONS; 272 this.features = FEATURES == null ? null : new JexlFeatures(FEATURES); 273 } 274 275 /** 276 * Is antish resolution enabled? 277 * 278 * @return whether antish resolution is enabled 279 */ 280 public boolean antish() { 281 return options.isAntish(); 282 } 283 284 /** 285 * Sets whether the engine will resolve antish variable names. 286 * 287 * @param flag true means antish resolution is enabled, false disables it 288 * @return this builder 289 */ 290 public JexlBuilder antish(final boolean flag) { 291 options.setAntish(flag); 292 return this; 293 } 294 295 /** 296 * Gets the JexlArithmetic instance the engine will use. 297 * 298 * @return The arithmetic 299 */ 300 public JexlArithmetic arithmetic() { 301 return this.arithmetic; 302 } 303 304 /** 305 * Sets the JexlArithmetic instance the engine will use. 306 * 307 * @param a The arithmetic 308 * @return this builder 309 */ 310 public JexlBuilder arithmetic(final JexlArithmetic a) { 311 this.arithmetic = a; 312 options.setStrictArithmetic(a.isStrict()); 313 options.setMathContext(a.getMathContext()); 314 options.setMathScale(a.getMathScale()); 315 return this; 316 } 317 318 /** 319 * Sets whether logical expressions ("" , ||) coerce their result to boolean. 320 * 321 * @param flag true or false 322 * @return this builder 323 */ 324 public JexlBuilder booleanLogical(final boolean flag) { 325 options.setBooleanLogical(flag); 326 return this; 327 } 328 329 /** 330 * Gets the expression cache size the engine will use. 331 * 332 * @return The cache size 333 */ 334 public int cache() { 335 return cache; 336 } 337 338 /** 339 * Sets the expression cache size the engine will use. 340 * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length. 341 * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p> 342 * 343 * @param size if not strictly positive, no cache is used. 344 * @return this builder 345 */ 346 public JexlBuilder cache(final int size) { 347 this.cache = size; 348 return this; 349 } 350 351 /** 352 * Gets the expression-cache factory the engine will use. 353 * 354 * @return The cache factory 355 */ 356 public IntFunction<JexlCache<?, ?>> cacheFactory() { 357 return this.cacheFactory; 358 } 359 360 /** 361 * Sets the expression-cache factory the engine will use. 362 * 363 * @param factory The function to produce a cache. 364 * @return this builder 365 */ 366 public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) { 367 this.cacheFactory = factory; 368 return this; 369 } 370 371 /** 372 * Gets the Jexl script parser factory the engine will use. 373 * 374 * @return The cache factory 375 * @since 3.5.0 376 */ 377 public Supplier<JexlScriptParser> parserFactory() { 378 return this.parserFactory; 379 } 380 381 /** 382 * Sets the Jexl script parser factory the engine will use. 383 * 384 * @param factory The function to produce a cache. 385 * @return this builder 386 * @since 3.5.0 387 */ 388 public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) { 389 this.parserFactory = factory; 390 return this; 391 } 392 393 /** 394 * Gets the maximum length for an expression to be cached. 395 * 396 * @return The cache threshold 397 */ 398 public int cacheThreshold() { 399 return cacheThreshold; 400 } 401 402 /** 403 * Sets the maximum length for an expression to be cached. 404 * <p>Expression whose length is greater than this expression cache length threshold will 405 * bypass the cache.</p> 406 * <p>It is expected that a "long" script will be parsed once and its reference kept 407 * around in user-space structures; the jexl expression cache has no added-value in this case.</p> 408 * 409 * @param length if not strictly positive, the value is silently replaced by the default value (64). 410 * @return this builder 411 */ 412 public JexlBuilder cacheThreshold(final int length) { 413 this.cacheThreshold = length > 0? length : CACHE_THRESHOLD; 414 return this; 415 } 416 417 /** 418 * Gets the cancellable information flag 419 * 420 * @return The cancellable information flag 421 * @since 3.1 422 */ 423 public Boolean cancellable() { 424 return this.cancellable; 425 } 426 427 /** 428 * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation 429 * and return null. 430 * 431 * @param flag true implies the engine throws the exception, false makes the engine return null. 432 * @return this builder 433 * @since 3.1 434 */ 435 public JexlBuilder cancellable(final boolean flag) { 436 this.cancellable = flag; 437 options.setCancellable(flag); 438 return this; 439 } 440 441 /** 442 * Gets the charset 443 * 444 * @return The charset 445 */ 446 public Charset charset() { 447 return charset; 448 } 449 450 /** 451 * Sets the charset to use. 452 * 453 * @param arg The charset 454 * @return this builder 455 * @since 3.1 456 */ 457 public JexlBuilder charset(final Charset arg) { 458 this.charset = arg; 459 return this; 460 } 461 462 /** 463 * Does the variable collection follow strict syntactic rule? 464 * 465 * @return true if variable collection follows strict syntactic rule 466 * @since 3.2 467 */ 468 public boolean collectAll() { 469 return this.collectMode != 0; 470 } 471 472 /** 473 * Sets whether the engine variable collectors considers all potential forms of variable syntaxes. 474 * 475 * @param flag true means var collections considers constant array accesses equivalent to dotted references 476 * @return this builder 477 * @since 3.2 478 */ 479 public JexlBuilder collectAll(final boolean flag) { 480 return collectMode(flag? 1 : 0); 481 } 482 483 /** 484 * Gets the collection mode. 485 * 486 * @return 0 if variable collection follows strict syntactic rule 487 * @since 3.2 488 */ 489 public int collectMode() { 490 return this.collectMode; 491 } 492 493 /** 494 * Experimental collector mode setter. 495 * 496 * @param mode 0 or 1 as equivalents to false and true, other values are experimental 497 * @return this builder 498 * @since 3.2 499 */ 500 public JexlBuilder collectMode(final int mode) { 501 this.collectMode = mode; 502 return this; 503 } 504 505 /** 506 * Create a new engine 507 * 508 * @return A {@link JexlEngine} instance 509 */ 510 public JexlEngine create() { 511 return new Engine(this); 512 } 513 514 /** 515 * Gets the debug flag. 516 * 517 * @return The debugging information flag 518 */ 519 public Boolean debug() { 520 return this.debug; 521 } 522 523 /** 524 * Sets whether the engine will report debugging information when error occurs. 525 * 526 * @see JexlEngine#isDebug() 527 * @param flag true implies debug is on, false implies debug is off. 528 * @return this builder 529 */ 530 public JexlBuilder debug(final boolean flag) { 531 this.debug = flag; 532 return this; 533 } 534 535 /** 536 * Gets the features the engine will use as a base by default. 537 * 538 * @return The features 539 */ 540 public JexlFeatures features() { 541 return this.features; 542 } 543 544 /** 545 * Sets the features the engine will use as a base by default. 546 * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts. 547 * <p>Note also that these will apply to template expressions and scripts. 548 * <p>As a last remark, if lexical or lexicalShade are set as features, this 549 * method will also set the corresponding options. 550 * 551 * @param f The features 552 * @return this builder 553 */ 554 public JexlBuilder features(final JexlFeatures f) { 555 this.features = f; 556 if (features != null) { 557 if (features.isLexical()) { 558 options.setLexical(true); 559 } 560 if (features.isLexicalShade()) { 561 options.setLexicalShade(true); 562 } 563 } 564 return this; 565 } 566 567 /** 568 * Gets the optional set of imported packages. 569 * 570 * @return The set of imports, may be empty, not null 571 */ 572 public Collection<String> imports() { 573 return options.getImports(); 574 } 575 576 /** 577 * Sets the optional set of imports. 578 * 579 * @param imports The imported packages 580 * @return this builder 581 */ 582 public JexlBuilder imports(final Collection<String> imports) { 583 options.setImports(imports); 584 return this; 585 } 586 587 /** 588 * Sets the optional set of imports. 589 * 590 * @param imports The imported packages 591 * @return this builder 592 */ 593 public JexlBuilder imports(final String... imports) { 594 return imports(Arrays.asList(imports)); 595 } 596 597 /** 598 * Is lexical scope enabled? 599 * 600 * @see JexlOptions#isLexical() 601 * @return whether lexical scope is enabled 602 * @deprecated 3.5.0 603 */ 604 @Deprecated 605 public boolean lexical() { 606 return options.isLexical(); 607 } 608 609 /** 610 * Sets whether the engine is in lexical mode. 611 * 612 * @param flag true means lexical function scope is in effect, false implies non-lexical scoping 613 * @return this builder 614 * @since 3.2 615 */ 616 public JexlBuilder lexical(final boolean flag) { 617 options.setLexical(flag); 618 return this; 619 } 620 621 /** 622 * Checks whether lexical shading is enabled. 623 * 624 * @see JexlOptions#isLexicalShade() 625 * @return whether lexical shading is enabled 626 * @deprecated 3.5.0 627 */ 628 @Deprecated 629 public boolean lexicalShade() { 630 return options.isLexicalShade(); 631 } 632 633 /** 634 * Sets whether the engine is in lexical shading mode. 635 * 636 * @param flag true means lexical shading is in effect; false implies no lexical shading 637 * @return this builder 638 * @since 3.2 639 */ 640 public JexlBuilder lexicalShade(final boolean flag) { 641 options.setLexicalShade(flag); 642 return this; 643 } 644 645 /** 646 * Gets the classloader 647 * 648 * @return The class loader 649 */ 650 public ClassLoader loader() { 651 return loader; 652 } 653 654 /** 655 * Sets the charset to use. 656 * 657 * @param arg The charset 658 * @return this builder 659 * @deprecated since 3.1 use {@link #charset(Charset)} instead 660 */ 661 @Deprecated 662 public JexlBuilder loader(final Charset arg) { 663 return charset(arg); 664 } 665 666 /** 667 * Sets the class loader to use. 668 * 669 * @param l The class loader 670 * @return this builder 671 */ 672 public JexlBuilder loader(final ClassLoader l) { 673 this.loader = l; 674 return this; 675 } 676 677 /** 678 * Gets the logger 679 * 680 * @return The logger 681 */ 682 public Log logger() { 683 return this.logger; 684 } 685 686 /** 687 * Sets the o.a.c.Log instance to use. 688 * 689 * @param log The logger 690 * @return this builder 691 */ 692 public JexlBuilder logger(final Log log) { 693 this.logger = log; 694 return this; 695 } 696 697 /** 698 * Gets the map of namespaces. 699 * 700 * @return The map of namespaces. 701 */ 702 public Map<String, Object> namespaces() { 703 return options.getNamespaces(); 704 } 705 706 /** 707 * Sets the default namespaces map the engine will use. 708 * <p> 709 * Each entry key is used as a prefix, each entry value used as a bean implementing 710 * methods; an expression like 'nsx:method(123)' will thus be solved by looking at 711 * a registered bean named 'nsx' that implements method 'method' in that map. 712 * If all methods are static, you may use the bean class instead of an instance as value. 713 * </p> 714 * <p> 715 * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance 716 * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext 717 * to carry the information used by the namespace to avoid variable space pollution and strongly type 718 * the constructor with this specialized JexlContext. 719 * </p> 720 * <p> 721 * The key or prefix allows to retrieve the bean that plays the role of the namespace. 722 * If the prefix is null, the namespace is the top-level namespace allowing to define 723 * top-level user-defined namespaces ( ie: myfunc(...) ) 724 * </p> 725 * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext 726 * derived instances to call methods on the wrapped object.</p> 727 * 728 * @param ns The map of namespaces 729 * @return this builder 730 */ 731 public JexlBuilder namespaces(final Map<String, Object> ns) { 732 options.setNamespaces(ns); 733 return this; 734 } 735 736 /** 737 * Gets the current set of options 738 * 739 * @return The current set of options 740 */ 741 public JexlOptions options() { 742 return options; 743 } 744 745 /** 746 * Gets the permissions 747 * 748 * @return The permissions 749 */ 750 public JexlPermissions permissions() { 751 return this.permissions; 752 } 753 754 /** 755 * Sets the JexlPermissions instance the engine will use. 756 * 757 * @param p The permissions 758 * @return this builder 759 */ 760 public JexlBuilder permissions(final JexlPermissions p) { 761 this.permissions = p; 762 return this; 763 } 764 765 /** 766 * Is it safe to dereference null? 767 * 768 * @return true if safe, false otherwise 769 */ 770 public Boolean safe() { 771 return options.isSafe(); 772 } 773 774 /** 775 * Sets whether the engine considers dereferencing null in navigation expressions 776 * as null or triggers an error. 777 * <p>{@code x.y()} if x is null throws an exception when not safe, 778 * return null and warns if it is.</p> 779 * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p> 780 * 781 * @param flag true means safe navigation, false throws exception when dereferencing null 782 * @return this builder 783 */ 784 public JexlBuilder safe(final boolean flag) { 785 options.setSafe(flag); 786 return this; 787 } 788 789 /** 790 * Gets the sandbox 791 * 792 * @return The sandbox 793 */ 794 public JexlSandbox sandbox() { 795 return this.sandbox; 796 } 797 798 /** 799 * Sets the sandbox the engine will use. 800 * 801 * @param box The sandbox 802 * @return this builder 803 */ 804 public JexlBuilder sandbox(final JexlSandbox box) { 805 this.sandbox = box; 806 return this; 807 } 808 809 /** 810 * Is error handling silent? 811 * 812 * @return The silent error handling flag 813 */ 814 public Boolean silent() { 815 return options.isSilent(); 816 } 817 818 /** 819 * Sets whether the engine will throw JexlException during evaluation when an error is triggered. 820 * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an 821 * error.</p> 822 * <p>It is recommended to use <em>silent(false)</em> as an explicit default.</p> 823 * 824 * @param flag true means no JexlException will occur, false allows them 825 * @return this builder 826 */ 827 public JexlBuilder silent(final boolean flag) { 828 options.setSilent(flag); 829 return this; 830 } 831 832 /** 833 * Gets the cache size 834 * 835 * @return The cache size 836 */ 837 public int stackOverflow() { 838 return stackOverflow; 839 } 840 841 /** 842 * Sets the number of script/expression evaluations that can be stacked. 843 * 844 * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown. 845 * @return this builder 846 */ 847 public JexlBuilder stackOverflow(final int size) { 848 this.stackOverflow = size; 849 return this; 850 } 851 852 /** 853 * Gets the JexlUberspect strategy 854 * 855 * @return The JexlUberspect strategy */ 856 public JexlUberspect.ResolverStrategy strategy() { 857 return this.strategy; 858 } 859 860 /** 861 * Sets the JexlUberspect strategy the engine will use. 862 * <p>This is ignored if the uberspect has been set. 863 * 864 * @param rs The strategy 865 * @return this builder 866 */ 867 public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) { 868 this.strategy = rs; 869 return this; 870 } 871 872 /** 873 * Is it strict mode? 874 * 875 * @return true if strict, false otherwise */ 876 public Boolean strict() { 877 return options.isStrict(); 878 } 879 880 /** 881 * Sets whether the engine considers unknown variables, methods, functions, and constructors as errors or 882 * evaluates them as null. 883 * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When 884 * strict, those raise exceptions.</p> 885 * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p> 886 * 887 * @param flag true means strict error reporting, false allows them to be evaluated as null 888 * @return this builder 889 */ 890 public JexlBuilder strict(final boolean flag) { 891 options.setStrict(flag); 892 return this; 893 } 894 895 /** 896 * Is interpolation strict? 897 * 898 * @see JexlOptions#setStrictInterpolation(boolean) 899 * @param flag strict interpolation flag 900 * @return this builder 901 */ 902 public JexlBuilder strictInterpolation(final boolean flag) { 903 options.setStrictInterpolation(flag); 904 return this; 905 } 906 907 /** 908 * Gets the uberspect 909 * 910 * @return The uberspect */ 911 public JexlUberspect uberspect() { 912 return this.uberspect; 913 } 914 915 /** 916 * Sets the JexlUberspect instance the engine will use. 917 * 918 * @param u The uberspect 919 * @return this builder 920 */ 921 public JexlBuilder uberspect(final JexlUberspect u) { 922 this.uberspect = u; 923 return this; 924 } 925}