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