001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jexl3;
019
020import java.nio.charset.Charset;
021import java.util.Arrays;
022import java.util.Collection;
023import java.util.Map;
024import java.util.function.IntFunction;
025import java.util.function.Supplier;
026
027import org.apache.commons.jexl3.internal.Engine;
028import org.apache.commons.jexl3.internal.SoftCache;
029import org.apache.commons.jexl3.introspection.JexlPermissions;
030import org.apache.commons.jexl3.introspection.JexlSandbox;
031import org.apache.commons.jexl3.introspection.JexlUberspect;
032import org.apache.commons.jexl3.parser.JexlScriptParser;
033import org.apache.commons.logging.Log;
034
035/**
036 * Configures and builds a JexlEngine.
037 *
038 * <p>
039 *     The builder allow fine-tuning an engine instance behavior according to various control needs.
040 *     Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>.
041 * </p><p>
042 *     Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL
043 *  syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control
044 *  the visible set of objects - for instance, avoiding access to any object in java.rmi.* -.
045 *  </p><p>
046 *     Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most
047 * common flags accessible from the builder are reflected in its options ({@link #options()}).
048 * </p><p>
049 *     The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as
050 * warning, when false, they throw {@link JexlException} exceptions.
051 * </p><p>
052 *     The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe}
053 * flog determines if safe-navigation is used. Safe-navigation allows an  evaluation shortcut and return null in expressions
054 * that attempts dereferencing null, typically a method call or accessing a property.
055 * </p><p>
056 *     The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for
057 * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be
058 * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be
059 * preferred since they will detect violations at parsing time. (see {@link JexlFeatures})
060 * </p><p>
061 *     The following rules apply on silent and strict flags:
062 * </p>
063 * <ul>
064 * <li>When "silent" &amp; "not-strict":
065 * <p> 0 &amp; null should be indicators of "default" values so that even in an case of error,
066 * something meaningful can still be inferred; may be convenient for configurations.
067 * </p>
068 * </li>
069 * <li>When "silent" &amp; "strict":
070 * <p>One should probably consider using null as an error case - ie, every object
071 * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
072 * can be used to workaround exceptional cases.
073 * Use case could be configuration with no implicit values or defaults.
074 * </p>
075 * </li>
076 * <li>When "not-silent" &amp; "not-strict":
077 * <p>The error control grain is roughly on par with JEXL 1.0</p>
078 * </li>
079 * <li>When "not-silent" &amp; "strict":
080 * <p>The finest error control grain is obtained; it is the closest to Java code -
081 * still augmented by "script" capabilities regarding automated conversions and type matching.
082 * </p>
083 * </li>
084 * </ul>
085 */
086public class JexlBuilder {
087
088    /**
089     * The set of default permissions used when creating a new builder.
090     * <p>Static but modifiable, so these default permissions can be changed to a purposeful set.</p>
091     * <p>In JEXL 3.7, these are {@link JexlPermissions#SECURE}.</p>
092     * <p>In JEXL 3.3, these were {@link JexlPermissions#RESTRICTED}.</p>
093     * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p>
094     */
095    private static JexlPermissions PERMISSIONS = JexlPermissions.SECURE;
096
097    /**
098     * The full feature set that was the implicit default before JEXL 3.7.
099     * <p>This captures {@link JexlFeatures#createDefault()} - the feature set the engine used as its
100     * fallback throughout 3.x (since 3.3) before the 3.7 hardening. It enables {@code new(...)}, loops,
101     * side-effects, lambdas, method calls, structured literals, pragmas, etc. Use it (or
102     * {@link #setDefaultFeatures(JexlFeatures)}) to restore pre-3.7 behavior:</p>
103     * <pre>
104     * JexlEngine jexl = new JexlBuilder().features(JexlBuilder.FULL).create();
105     * </pre>
106     * <p>{@link JexlFeatures} instances are mutable; copy this one via {@code new JexlFeatures(JexlBuilder.FULL)}
107     * before customizing it rather than mutating the shared instance.</p>
108     *
109     * @since 3.7.0
110     */
111    public static final JexlFeatures FULL = JexlFeatures.createDefault();
112
113    /**
114     * The set of default features used when creating a new builder.
115     * <p>Static but modifiable via {@link #setDefaultFeatures(JexlFeatures)}.</p>
116     * <p>In JEXL 3.7, the default hardens the feature set relative to {@link #FULL}: {@code new(...)},
117     * global side-effects, pragmas, and annotations are disabled, and lexical scoping (with shade) is
118     * enabled.  Loops remain available to scripts (expressions never allow them).  Scripts using a
119     * disabled construct will throw {@link JexlException.Feature} at parse time.</p>
120     *
121     * @see #secureFeatures()
122     */
123    private static JexlFeatures FEATURES = secureFeatures();
124
125    /** The default maximum expression length to hit the expression cache. */
126    protected static final int CACHE_THRESHOLD = 64;
127
128    /**
129     * Sets the default permissions.
130     *
131     * @param permissions the permissions
132     */
133    public static void setDefaultPermissions(final JexlPermissions permissions) {
134        PERMISSIONS = permissions == null ? JexlPermissions.SECURE : permissions;
135    }
136
137    /**
138     * Sets the default features used when creating a new builder.
139     * <p>Passing {@code null} restores the 3.7 hardened default (new-instance, global side-effects,
140     * pragmas, and annotations disabled, lexical scoping enabled; see {@link #secureFeatures()}).</p>
141     *
142     * @param features the features, or {@code null} to restore the hardened default
143     * @since 3.7.0
144     */
145    public static void setDefaultFeatures(final JexlFeatures features) {
146        FEATURES = features == null ? secureFeatures() : features;
147    }
148
149    /**
150     * Sets the default option flags used when creating a new engine.
151     * <p>Delegates to {@link JexlOptions#setDefaultFlags(String...)}; see that method for the
152     * flag-name syntax ({@code "+flag"}/{@code "-flag"}) and the available flag names
153     * ({@code cancellable}, {@code strict}, {@code silent}, {@code safe}, {@code lexical},
154     * {@code antish}, {@code lexicalShade}, ...).</p>
155     * <p>Example: restore safe/antish defaults before creating any engine:</p>
156     * <pre>JexlBuilder.setDefaultOptions("+safe", "+antish");</pre>
157     *
158     * @param flags the flags to set or clear
159     * @since 3.7.0
160     */
161    public static void setDefaultOptions(final String... flags) {
162        JexlOptions.setDefaultFlags(flags);
163    }
164
165    /**
166     * Builds the JEXL 3.7 hardened default feature set.
167     * <p>Starting from {@link #FULL} (the pre-3.7 feature set), this disables {@code new(...)},
168     * global side-effects, pragmas, and annotations, and enables lexical scoping and lexical shade.
169     * Loops are left enabled so scripts can iterate; expressions never allow loops since they are
170     * parsed as a single expression.</p>
171     *
172     * @return a fresh hardened feature set
173     * @since 3.7.0
174     */
175    private static JexlFeatures secureFeatures() {
176        return new JexlFeatures(FULL)
177            .newInstance(false)
178            .sideEffectGlobal(false)
179            .pragma(false)
180            .annotation(false)
181            .loops(true)
182            .lexical(true)
183            .lexicalShade(true);
184    }
185
186    /** The JexlUberspect instance. */
187    private JexlUberspect uberspect;
188
189    /** The {@link JexlUberspect} resolver strategy. */
190    private JexlUberspect.ResolverStrategy strategy;
191
192    /** The set of permissions. */
193    private JexlPermissions permissions;
194
195    /** The sandbox. */
196    private JexlSandbox sandbox;
197
198    /** The Log to which all JexlEngine messages will be logged. */
199    private Log logger;
200
201    /** Whether error messages will carry debugging information. */
202    private Boolean debug;
203
204    /** Whether interrupt throws JexlException.Cancel. */
205    private Boolean cancellable;
206
207    /** The options. */
208    private final JexlOptions options = new JexlOptions();
209
210    /** Whether getVariables considers all potential equivalent syntactic forms. */
211    private int collectMode = 1;
212
213    /** The {@link JexlArithmetic} instance. */
214    private JexlArithmetic arithmetic;
215
216    /** The cache size. */
217    private int cache = -1;
218
219    /** The cache class factory. */
220    private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new;
221
222   /** The parser class factory. */
223   private Supplier<JexlScriptParser> parserFactory;
224
225    /** The stack overflow limit. */
226    private int stackOverflow = Integer.MAX_VALUE;
227
228    /** The maximum expression length to hit the expression cache. */
229    private int cacheThreshold = CACHE_THRESHOLD;
230
231    /** The charset. */
232    private Charset charset = Charset.defaultCharset();
233
234    /** The class loader. */
235    private ClassLoader loader;
236
237    /** The features. */
238    private JexlFeatures features;
239
240    /**
241     * Default constructor.
242     * <p>
243     * As of JEXL 3.3, to reduce the security risks inherent to JEXL&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}