View Javadoc
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" &amp; "not-strict":
65   * <p> 0 &amp; 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" &amp; "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" &amp; "not-strict":
77   * <p>The error control grain is roughly on par with JEXL 1.0</p>
78   * </li>
79   * <li>When "not-silent" &amp; "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&quot;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 (&quot;&quot; , ||) 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 }