1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.jexl3;
19
20 import java.nio.charset.Charset;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.function.IntFunction;
25 import java.util.function.Supplier;
26
27 import org.apache.commons.jexl3.internal.Engine;
28 import org.apache.commons.jexl3.internal.SoftCache;
29 import org.apache.commons.jexl3.introspection.JexlPermissions;
30 import org.apache.commons.jexl3.introspection.JexlSandbox;
31 import org.apache.commons.jexl3.introspection.JexlUberspect;
32 import org.apache.commons.jexl3.parser.JexlScriptParser;
33 import org.apache.commons.logging.Log;
34
35 /**
36 * Configures and builds a JexlEngine.
37 *
38 * <p>
39 * The builder allow fine-tuning an engine instance behavior according to various control needs.
40 * Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>.
41 * </p><p>
42 * Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL
43 * syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control
44 * the visible set of objects - for instance, avoiding access to any object in java.rmi.* -.
45 * </p><p>
46 * Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most
47 * common flags accessible from the builder are reflected in its options ({@link #options()}).
48 * </p><p>
49 * The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as
50 * warning, when false, they throw {@link JexlException} exceptions.
51 * </p><p>
52 * The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe}
53 * flog determines if safe-navigation is used. Safe-navigation allows an evaluation shortcut and return null in expressions
54 * that attempts dereferencing null, typically a method call or accessing a property.
55 * </p><p>
56 * The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for
57 * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be
58 * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be
59 * preferred since they will detect violations at parsing time. (see {@link JexlFeatures})
60 * </p><p>
61 * The following rules apply on silent and strict flags:
62 * </p>
63 * <ul>
64 * <li>When "silent" & "not-strict":
65 * <p> 0 & null should be indicators of "default" values so that even in an case of error,
66 * something meaningful can still be inferred; may be convenient for configurations.
67 * </p>
68 * </li>
69 * <li>When "silent" & "strict":
70 * <p>One should probably consider using null as an error case - ie, every object
71 * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
72 * can be used to workaround exceptional cases.
73 * Use case could be configuration with no implicit values or defaults.
74 * </p>
75 * </li>
76 * <li>When "not-silent" & "not-strict":
77 * <p>The error control grain is roughly on par with JEXL 1.0</p>
78 * </li>
79 * <li>When "not-silent" & "strict":
80 * <p>The finest error control grain is obtained; it is the closest to Java code -
81 * still augmented by "script" capabilities regarding automated conversions and type matching.
82 * </p>
83 * </li>
84 * </ul>
85 */
86 public class JexlBuilder {
87
88 /**
89 * The set of default permissions used when creating a new builder.
90 * <p>Static but modifiable, so these default permissions can be changed to a purposeful set.</p>
91 * <p>In JEXL 3.7, these are {@link JexlPermissions#SECURE}.</p>
92 * <p>In JEXL 3.3, these were {@link JexlPermissions#RESTRICTED}.</p>
93 * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p>
94 */
95 private static JexlPermissions PERMISSIONS = JexlPermissions.SECURE;
96
97 /**
98 * The full feature set that was the implicit default before JEXL 3.7.
99 * <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 }