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, 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 }