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, and annotations, 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 } 185 186 /** The JexlUberspect instance. */ 187 private JexlUberspect uberspect; 188 189 /** The {@link JexlUberspect} resolver strategy. */ 190 private JexlUberspect.ResolverStrategy strategy; 191 192 /** The set of permissions. */ 193 private JexlPermissions permissions; 194 195 /** The sandbox. */ 196 private JexlSandbox sandbox; 197 198 /** The Log to which all JexlEngine messages will be logged. */ 199 private Log logger; 200 201 /** Whether error messages will carry debugging information. */ 202 private Boolean debug; 203 204 /** Whether interrupt throws JexlException.Cancel. */ 205 private Boolean cancellable; 206 207 /** The options. */ 208 private final JexlOptions options = new JexlOptions(); 209 210 /** Whether getVariables considers all potential equivalent syntactic forms. */ 211 private int collectMode = 1; 212 213 /** The {@link JexlArithmetic} instance. */ 214 private JexlArithmetic arithmetic; 215 216 /** The cache size. */ 217 private int cache = -1; 218 219 /** The cache class factory. */ 220 private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new; 221 222 /** The parser class factory. */ 223 private Supplier<JexlScriptParser> parserFactory; 224 225 /** The stack overflow limit. */ 226 private int stackOverflow = Integer.MAX_VALUE; 227 228 /** The maximum expression length to hit the expression cache. */ 229 private int cacheThreshold = CACHE_THRESHOLD; 230 231 /** The charset. */ 232 private Charset charset = Charset.defaultCharset(); 233 234 /** The class loader. */ 235 private ClassLoader loader; 236 237 /** The features. */ 238 private JexlFeatures features; 239 240 /** 241 * Default constructor. 242 * <p> 243 * As of JEXL 3.3, to reduce the security risks inherent to JEXL"s purpose, the builder will use a set of 244 * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes 245 * and methods are visible to JEXL and usable in scripts using default implicit behaviors. 246 * </p><p> 247 * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing 248 * your own class instances through arguments, contexts, or namespaces. 249 * The default set of allowed packages and denied classes is described by {@link JexlPermissions#SECURE} 250 * as of JEXL 3.7 (previously {@link JexlPermissions#RESTRICTED} since 3.3). 251 * </p><p> 252 * As of JEXL 3.7, the default features also disable {@code new(...)}, global side-effects, pragmas, 253 * and annotations, and enable lexical scoping; loops remain available to scripts (but never to 254 * expressions). Scripts using a disabled construct will throw {@link JexlException.Feature} at parse 255 * time unless a permissive feature set is configured. 256 * </p><p> 257 * The recommended mitigation if your usage of JEXL is impacted is to load a {@code jexl.yaml} 258 * configuration via {@link JexlConfigLoader} that restores the legacy permissions and features, 259 * or to explicitly configure them via {@link #permissions(JexlPermissions)} and 260 * {@link #features(JexlFeatures)}. Use {@link JexlPermissions#logging()} to discover which 261 * reflective elements the new algorithm denies. 262 * </p><p> 263 * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior 264 * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}. 265 * </p> 266 * 267 * @since 3.3 268 */ 269 public JexlBuilder() { 270 this.permissions = PERMISSIONS; 271 this.features = FEATURES == null ? null : new JexlFeatures(FEATURES); 272 } 273 274 /** 275 * Is antish resolution enabled? 276 * 277 * @return whether antish resolution is enabled 278 */ 279 public boolean antish() { 280 return options.isAntish(); 281 } 282 283 /** 284 * Sets whether the engine will resolve antish variable names. 285 * 286 * @param flag true means antish resolution is enabled, false disables it 287 * @return this builder 288 */ 289 public JexlBuilder antish(final boolean flag) { 290 options.setAntish(flag); 291 return this; 292 } 293 294 /** 295 * Gets the JexlArithmetic instance the engine will use. 296 * 297 * @return the arithmetic 298 */ 299 public JexlArithmetic arithmetic() { 300 return this.arithmetic; 301 } 302 303 /** 304 * Sets the JexlArithmetic instance the engine will use. 305 * 306 * @param a the arithmetic 307 * @return this builder 308 */ 309 public JexlBuilder arithmetic(final JexlArithmetic a) { 310 this.arithmetic = a; 311 options.setStrictArithmetic(a.isStrict()); 312 options.setMathContext(a.getMathContext()); 313 options.setMathScale(a.getMathScale()); 314 return this; 315 } 316 317 /** 318 * Sets whether logical expressions ("" , ||) coerce their result to boolean. 319 * 320 * @param flag true or false 321 * @return this builder 322 */ 323 public JexlBuilder booleanLogical(final boolean flag) { 324 options.setBooleanLogical(flag); 325 return this; 326 } 327 328 /** 329 * Gets the expression cache size the engine will use. 330 * 331 * @return the cache size 332 */ 333 public int cache() { 334 return cache; 335 } 336 337 /** 338 * Sets the expression cache size the engine will use. 339 * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length. 340 * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p> 341 * 342 * @param size if not strictly positive, no cache is used. 343 * @return this builder 344 */ 345 public JexlBuilder cache(final int size) { 346 this.cache = size; 347 return this; 348 } 349 350 /** 351 * Gets the expression-cache factory the engine will use. 352 * 353 * @return the cache factory 354 */ 355 public IntFunction<JexlCache<?, ?>> cacheFactory() { 356 return this.cacheFactory; 357 } 358 359 /** 360 * Sets the expression-cache factory the engine will use. 361 * 362 * @param factory the function to produce a cache. 363 * @return this builder 364 */ 365 public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) { 366 this.cacheFactory = factory; 367 return this; 368 } 369 370 /** 371 * Gets the Jexl script parser factory the engine will use. 372 * 373 * @return the cache factory 374 * @since 3.5.0 375 */ 376 public Supplier<JexlScriptParser> parserFactory() { 377 return this.parserFactory; 378 } 379 380 /** 381 * Sets the Jexl script parser factory the engine will use. 382 * 383 * @param factory the function to produce a cache. 384 * @return this builder 385 * @since 3.5.0 386 */ 387 public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) { 388 this.parserFactory = factory; 389 return this; 390 } 391 392 /** 393 * Gets the maximum length for an expression to be cached. 394 * 395 * @return the cache threshold 396 */ 397 public int cacheThreshold() { 398 return cacheThreshold; 399 } 400 401 /** 402 * Sets the maximum length for an expression to be cached. 403 * <p>Expression whose length is greater than this expression cache length threshold will 404 * bypass the cache.</p> 405 * <p>It is expected that a "long" script will be parsed once and its reference kept 406 * around in user-space structures; the jexl expression cache has no added-value in this case.</p> 407 * 408 * @param length if not strictly positive, the value is silently replaced by the default value (64). 409 * @return this builder 410 */ 411 public JexlBuilder cacheThreshold(final int length) { 412 this.cacheThreshold = length > 0? length : CACHE_THRESHOLD; 413 return this; 414 } 415 416 /** 417 * Gets the cancellable information flag 418 * 419 * @return the cancellable information flag 420 * @since 3.1 421 */ 422 public Boolean cancellable() { 423 return this.cancellable; 424 } 425 426 /** 427 * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation 428 * and return null. 429 * 430 * @param flag true implies the engine throws the exception, false makes the engine return null. 431 * @return this builder 432 * @since 3.1 433 */ 434 public JexlBuilder cancellable(final boolean flag) { 435 this.cancellable = flag; 436 options.setCancellable(flag); 437 return this; 438 } 439 440 /** 441 * Gets the charset 442 * 443 * @return the charset 444 */ 445 public Charset charset() { 446 return charset; 447 } 448 449 /** 450 * Sets the charset to use. 451 * 452 * @param arg the charset 453 * @return this builder 454 * @since 3.1 455 */ 456 public JexlBuilder charset(final Charset arg) { 457 this.charset = arg; 458 return this; 459 } 460 461 /** 462 * Does the variable collection follow strict syntactic rule? 463 * 464 * @return true if variable collection follows strict syntactic rule 465 * @since 3.2 466 */ 467 public boolean collectAll() { 468 return this.collectMode != 0; 469 } 470 471 /** 472 * Sets whether the engine variable collectors considers all potential forms of variable syntaxes. 473 * 474 * @param flag true means var collections considers constant array accesses equivalent to dotted references 475 * @return this builder 476 * @since 3.2 477 */ 478 public JexlBuilder collectAll(final boolean flag) { 479 return collectMode(flag? 1 : 0); 480 } 481 482 /** 483 * Gets the collection mode. 484 * 485 * @return 0 if variable collection follows strict syntactic rule 486 * @since 3.2 487 */ 488 public int collectMode() { 489 return this.collectMode; 490 } 491 492 /** 493 * Experimental collector mode setter. 494 * 495 * @param mode 0 or 1 as equivalents to false and true, other values are experimental 496 * @return this builder 497 * @since 3.2 498 */ 499 public JexlBuilder collectMode(final int mode) { 500 this.collectMode = mode; 501 return this; 502 } 503 504 /** 505 * Create a new engine 506 * 507 * @return a {@link JexlEngine} instance 508 */ 509 public JexlEngine create() { 510 return new Engine(this); 511 } 512 513 /** 514 * Gets the debug flag. 515 * 516 * @return the debugging information flag 517 */ 518 public Boolean debug() { 519 return this.debug; 520 } 521 522 /** 523 * Sets whether the engine will report debugging information when error occurs. 524 * 525 * @see JexlEngine#isDebug() 526 * @param flag true implies debug is on, false implies debug is off. 527 * @return this builder 528 */ 529 public JexlBuilder debug(final boolean flag) { 530 this.debug = flag; 531 return this; 532 } 533 534 /** 535 * Gets the features the engine will use as a base by default. 536 * 537 * @return the features 538 */ 539 public JexlFeatures features() { 540 return this.features; 541 } 542 543 /** 544 * Sets the features the engine will use as a base by default. 545 * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts. 546 * <p>Note also that these will apply to template expressions and scripts. 547 * <p>As a last remark, if lexical or lexicalShade are set as features, this 548 * method will also set the corresponding options. 549 * 550 * @param f the features 551 * @return this builder 552 */ 553 public JexlBuilder features(final JexlFeatures f) { 554 this.features = f; 555 if (features != null) { 556 if (features.isLexical()) { 557 options.setLexical(true); 558 } 559 if (features.isLexicalShade()) { 560 options.setLexicalShade(true); 561 } 562 } 563 return this; 564 } 565 566 /** 567 * Gets the optional set of imported packages. 568 * 569 * @return the set of imports, may be empty, not null 570 */ 571 public Collection<String> imports() { 572 return options.getImports(); 573 } 574 575 /** 576 * Sets the optional set of imports. 577 * 578 * @param imports the imported packages 579 * @return this builder 580 */ 581 public JexlBuilder imports(final Collection<String> imports) { 582 options.setImports(imports); 583 return this; 584 } 585 586 /** 587 * Sets the optional set of imports. 588 * 589 * @param imports the imported packages 590 * @return this builder 591 */ 592 public JexlBuilder imports(final String... imports) { 593 return imports(Arrays.asList(imports)); 594 } 595 596 /** 597 * Is lexical scope enabled? 598 * 599 * @see JexlOptions#isLexical() 600 * @return whether lexical scope is enabled 601 * @deprecated 3.5.0 602 */ 603 @Deprecated 604 public boolean lexical() { 605 return options.isLexical(); 606 } 607 608 /** 609 * Sets whether the engine is in lexical mode. 610 * 611 * @param flag true means lexical function scope is in effect, false implies non-lexical scoping 612 * @return this builder 613 * @since 3.2 614 */ 615 public JexlBuilder lexical(final boolean flag) { 616 options.setLexical(flag); 617 return this; 618 } 619 620 /** 621 * Checks whether lexical shading is enabled. 622 * 623 * @see JexlOptions#isLexicalShade() 624 * @return whether lexical shading is enabled 625 * @deprecated 3.5.0 626 */ 627 @Deprecated 628 public boolean lexicalShade() { 629 return options.isLexicalShade(); 630 } 631 632 /** 633 * Sets whether the engine is in lexical shading mode. 634 * 635 * @param flag true means lexical shading is in effect; false implies no lexical shading 636 * @return this builder 637 * @since 3.2 638 */ 639 public JexlBuilder lexicalShade(final boolean flag) { 640 options.setLexicalShade(flag); 641 return this; 642 } 643 644 /** 645 * Gets the classloader 646 * 647 * @return the class loader 648 */ 649 public ClassLoader loader() { 650 return loader; 651 } 652 653 /** 654 * Sets the charset to use. 655 * 656 * @param arg the charset 657 * @return this builder 658 * @deprecated since 3.1 use {@link #charset(Charset)} instead 659 */ 660 @Deprecated 661 public JexlBuilder loader(final Charset arg) { 662 return charset(arg); 663 } 664 665 /** 666 * Sets the class loader to use. 667 * 668 * @param l the class loader 669 * @return this builder 670 */ 671 public JexlBuilder loader(final ClassLoader l) { 672 this.loader = l; 673 return this; 674 } 675 676 /** 677 * Gets the logger 678 * 679 * @return the logger 680 */ 681 public Log logger() { 682 return this.logger; 683 } 684 685 /** 686 * Sets the o.a.c.Log instance to use. 687 * 688 * @param log the logger 689 * @return this builder 690 */ 691 public JexlBuilder logger(final Log log) { 692 this.logger = log; 693 return this; 694 } 695 696 /** 697 * Gets the map of namespaces. 698 * 699 * @return the map of namespaces. 700 */ 701 public Map<String, Object> namespaces() { 702 return options.getNamespaces(); 703 } 704 705 /** 706 * Sets the default namespaces map the engine will use. 707 * <p> 708 * Each entry key is used as a prefix, each entry value used as a bean implementing 709 * methods; an expression like 'nsx:method(123)' will thus be solved by looking at 710 * a registered bean named 'nsx' that implements method 'method' in that map. 711 * If all methods are static, you may use the bean class instead of an instance as value. 712 * </p> 713 * <p> 714 * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance 715 * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext 716 * to carry the information used by the namespace to avoid variable space pollution and strongly type 717 * the constructor with this specialized JexlContext. 718 * </p> 719 * <p> 720 * The key or prefix allows to retrieve the bean that plays the role of the namespace. 721 * If the prefix is null, the namespace is the top-level namespace allowing to define 722 * top-level user-defined namespaces ( ie: myfunc(...) ) 723 * </p> 724 * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext 725 * derived instances to call methods on the wrapped object.</p> 726 * 727 * @param ns the map of namespaces 728 * @return this builder 729 */ 730 public JexlBuilder namespaces(final Map<String, Object> ns) { 731 options.setNamespaces(ns); 732 return this; 733 } 734 735 /** 736 * Gets the current set of options 737 * 738 * @return the current set of options 739 */ 740 public JexlOptions options() { 741 return options; 742 } 743 744 /** 745 * Gets the permissions 746 * 747 * @return the permissions 748 */ 749 public JexlPermissions permissions() { 750 return this.permissions; 751 } 752 753 /** 754 * Sets the JexlPermissions instance the engine will use. 755 * 756 * @param p the permissions 757 * @return this builder 758 */ 759 public JexlBuilder permissions(final JexlPermissions p) { 760 this.permissions = p; 761 return this; 762 } 763 764 /** 765 * Is it safe to dereference null? 766 * 767 * @return true if safe, false otherwise 768 */ 769 public Boolean safe() { 770 return options.isSafe(); 771 } 772 773 /** 774 * Sets whether the engine considers dereferencing null in navigation expressions 775 * as null or triggers an error. 776 * <p>{@code x.y()} if x is null throws an exception when not safe, 777 * return null and warns if it is.</p> 778 * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p> 779 * 780 * @param flag true means safe navigation, false throws exception when dereferencing null 781 * @return this builder 782 */ 783 public JexlBuilder safe(final boolean flag) { 784 options.setSafe(flag); 785 return this; 786 } 787 788 /** 789 * Gets the sandbox 790 * 791 * @return the sandbox 792 */ 793 public JexlSandbox sandbox() { 794 return this.sandbox; 795 } 796 797 /** 798 * Sets the sandbox the engine will use. 799 * 800 * @param box the sandbox 801 * @return this builder 802 */ 803 public JexlBuilder sandbox(final JexlSandbox box) { 804 this.sandbox = box; 805 return this; 806 } 807 808 /** 809 * Is error handling silent? 810 * 811 * @return the silent error handling flag 812 */ 813 public Boolean silent() { 814 return options.isSilent(); 815 } 816 817 /** 818 * Sets whether the engine will throw JexlException during evaluation when an error is triggered. 819 * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an 820 * error.</p> 821 * <p>It is recommended to use <em>silent(false)</em> as an explicit default.</p> 822 * 823 * @param flag true means no JexlException will occur, false allows them 824 * @return this builder 825 */ 826 public JexlBuilder silent(final boolean flag) { 827 options.setSilent(flag); 828 return this; 829 } 830 831 /** 832 * Gets the cache size 833 * 834 * @return the cache size 835 */ 836 public int stackOverflow() { 837 return stackOverflow; 838 } 839 840 /** 841 * Sets the number of script/expression evaluations that can be stacked. 842 * 843 * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown. 844 * @return this builder 845 */ 846 public JexlBuilder stackOverflow(final int size) { 847 this.stackOverflow = size; 848 return this; 849 } 850 851 /** 852 * Gets the JexlUberspect strategy 853 * 854 * @return the JexlUberspect strategy */ 855 public JexlUberspect.ResolverStrategy strategy() { 856 return this.strategy; 857 } 858 859 /** 860 * Sets the JexlUberspect strategy the engine will use. 861 * <p>This is ignored if the uberspect has been set. 862 * 863 * @param rs the strategy 864 * @return this builder 865 */ 866 public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) { 867 this.strategy = rs; 868 return this; 869 } 870 871 /** 872 * Is it strict mode? 873 * 874 * @return true if strict, false otherwise */ 875 public Boolean strict() { 876 return options.isStrict(); 877 } 878 879 /** 880 * Sets whether the engine considers unknown variables, methods, functions, and constructors as errors or 881 * evaluates them as null. 882 * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When 883 * strict, those raise exceptions.</p> 884 * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p> 885 * 886 * @param flag true means strict error reporting, false allows them to be evaluated as null 887 * @return this builder 888 */ 889 public JexlBuilder strict(final boolean flag) { 890 options.setStrict(flag); 891 return this; 892 } 893 894 /** 895 * Is interpolation strict? 896 * 897 * @see JexlOptions#setStrictInterpolation(boolean) 898 * @param flag strict interpolation flag 899 * @return this builder 900 */ 901 public JexlBuilder strictInterpolation(final boolean flag) { 902 options.setStrictInterpolation(flag); 903 return this; 904 } 905 906 /** 907 * Gets the uberspect 908 * 909 * @return the uberspect */ 910 public JexlUberspect uberspect() { 911 return this.uberspect; 912 } 913 914 /** 915 * Sets the JexlUberspect instance the engine will use. 916 * 917 * @param u the uberspect 918 * @return this builder 919 */ 920 public JexlBuilder uberspect(final JexlUberspect u) { 921 this.uberspect = u; 922 return this; 923 } 924}