    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     * The set of default permissions used when creating a new builder.
    089     * <p>Static but modifiable so these default permissions can be changed to a purposeful set.</p>
    090     * <p>In JEXL 3.3, these are {@link JexlPermissions#RESTRICTED}.</p>
    091     * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p>
    092     */
    093    private static JexlPermissions PERMISSIONS = JexlPermissions.RESTRICTED;
    094
    095    /** The default maximum expression length to hit the expression cache. */
    096    protected static final int CACHE_THRESHOLD = 64;
    097
    098    /**
    099     * Sets the default permissions.
    100     * @param permissions the permissions
    101     */
    102    public static void setDefaultPermissions(final JexlPermissions permissions) {
    103        PERMISSIONS = permissions == null ? JexlPermissions.RESTRICTED : permissions;
    104    }
    105
    106    /** The JexlUberspect instance. */
    107    private JexlUberspect uberspect;
    108
    109    /** The {@link JexlUberspect} resolver strategy. */
    110    private JexlUberspect.ResolverStrategy strategy;
    111
    112    /** The set of permissions. */
    113    private JexlPermissions permissions;
    114
    115    /** The sandbox. */
    116    private JexlSandbox sandbox;
    117
    118    /** The Log to which all JexlEngine messages will be logged. */
    119    private Log logger;
    120
    121    /** Whether error messages will carry debugging information. */
    122    private Boolean debug;
    123
    124    /** Whether interrupt throws JexlException.Cancel. */
    125    private Boolean cancellable;
    126
    127    /** The options. */
    128    private final JexlOptions options = new JexlOptions();
    129
    130    /** Whether getVariables considers all potential equivalent syntactic forms. */
    131    private int collectMode = 1;
    132
    133    /** The {@link JexlArithmetic} instance. */
    134    private JexlArithmetic arithmetic;
    135
    136    /** The cache size. */
    137    private int cache = -1;
    138
    139    /** The cache class factory. */
    140    private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new;
    141
    142   /** The parser class factory. */
    143   private Supplier<JexlScriptParser> parserFactory;
    144
    145    /** The stack overflow limit. */
    146    private int stackOverflow = Integer.MAX_VALUE;
    147
    148    /** The maximum expression length to hit the expression cache. */
    149    private int cacheThreshold = CACHE_THRESHOLD;
    150
    151    /** The charset. */
    152    private Charset charset = Charset.defaultCharset();
    153
    154    /** The class loader. */
    155    private ClassLoader loader;
    156
    157    /** The features. */
    158    private JexlFeatures features;
    159
    160    /**
    161     * Default constructor.
    162     * <p>
    163     * As of JEXL 3.3, to reduce the security risks inherent to JEXL&quot;s purpose, the builder will use a set of
    164     * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes
    165     * and methods are visible to JEXL and usable in scripts using default implicit behaviors.
    166     * </p><p>
    167     * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing
    168     * your own class instances through arguments, contexts or namespaces.
    169     * The new default set of allowed packages and denied classes is described by {@link JexlPermissions#RESTRICTED}.
    170     * </p><p>
    171     * The recommended mitigation if your usage of JEXL is impacted is to first thoroughly review what should be
    172     * allowed and exposed to script authors and implement those through a set of {@link JexlPermissions};
    173     * those are easily created using {@link JexlPermissions#parse(String...)}.
    174     * </p><p>
    175     * In the urgent case of a strict 3.2 compatibility, the simplest and fastest mitigation is to use the 'unrestricted'
    176     * set of permissions. The builder must be explicit about it either by setting the default permissions with a
    177     * statement like {@code JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);} or with a more precise
    178     * one like {@code new JexlBuilder().permissions({@link JexlPermissions#UNRESTRICTED})}.
    179     * </p><p>
    180     * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior
    181     * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}.
    182     * </p>
    183     * @since 3.3
    184     */
    185    public JexlBuilder() {
    186        this.permissions = PERMISSIONS;
    187    }
    188
    189    /**
    190     * Is antish resolution enabled?
    191     * @return whether antish resolution is enabled
    192     */
    193    public boolean antish() {
    194        return options.isAntish();
    195    }
    196
    197    /**
    198     * Sets whether the engine will resolve antish variable names.
    199     *
    200     * @param flag true means antish resolution is enabled, false disables it
    201     * @return this builder
    202     */
    203    public JexlBuilder antish(final boolean flag) {
    204        options.setAntish(flag);
    205        return this;
    206    }
    207
    208    /**
    209     * Gets the JexlArithmetic instance the engine will use.
    210     * @return the arithmetic
    211     */
    212    public JexlArithmetic arithmetic() {
    213        return this.arithmetic;
    214    }
    215
    216    /**
    217     * Sets the JexlArithmetic instance the engine will use.
    218     *
    219     * @param a the arithmetic
    220     * @return this builder
    221     */
    222    public JexlBuilder arithmetic(final JexlArithmetic a) {
    223        this.arithmetic = a;
    224        options.setStrictArithmetic(a.isStrict());
    225        options.setMathContext(a.getMathContext());
    226        options.setMathScale(a.getMathScale());
    227        return this;
    228    }
    229
    230    /**
    231     * Sets whether logical expressions (&quot;&quot; , ||) coerce their result to boolean.
    232     * @param flag true or false
    233     * @return this builder
    234     */
    235    public JexlBuilder booleanLogical(final boolean flag) {
    236      options.setBooleanLogical(flag);
    237      return this;
    238    }
    239
    240    /**
    241     * Gets the expression cache size the engine will use.
    242     * @return the cache size
    243     */
    244    public int cache() {
    245      return cache;
    246    }
    247
    248    /**
    249     * Sets the expression cache size the engine will use.
    250     * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length.
    251     * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p>
    252     *
    253     * @param size if not strictly positive, no cache is used.
    254     * @return this builder
    255     */
    256    public JexlBuilder cache(final int size) {
    257        this.cache = size;
    258        return this;
    259    }
    260
    261    /**
    262     * Gets the expression-cache factory the engine will use.
    263     * @return the cache factory
    264     */
    265    public IntFunction<JexlCache<?, ?>> cacheFactory() {
    266      return this.cacheFactory;
    267    }
    268
    269    /**
    270     * Sets the expression-cache factory the engine will use.
    271     *
    272     * @param factory the function to produce a cache.
    273     * @return this builder
    274     */
    275    public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) {
    276      this.cacheFactory = factory;
    277      return this;
    278    }
    279
    280  /**
    281   * Gets the Jexl script parser factory the engine will use.
    282   * @return the cache factory
    283   * @since 3.5.0
    284   */
    285  public Supplier<JexlScriptParser> parserFactory() {
    286    return this.parserFactory;
    287  }
    288
    289  /**
    290   * Sets the Jexl script parser factory the engine will use.
    291   *
    292   * @param factory the function to produce a cache.
    293   * @return this builder
    294   * @since 3.5.0
    295   */
    296    public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) {
    297      this.parserFactory = factory;
    298      return this;
    299    }
    300
    301    /**
    302     * Gets the maximum length for an expression to be cached.
    303     * @return the cache threshold
    304     */
    305    public int cacheThreshold() {
    306        return cacheThreshold;
    307    }
    308
    309    /**
    310     * Sets the maximum length for an expression to be cached.
    311     * <p>Expression whose length is greater than this expression cache length threshold will
    312     * bypass the cache.</p>
    313     * <p>It is expected that a "long" script will be parsed once and its reference kept
    314     * around in user-space structures; the jexl expression cache has no added-value in this case.</p>
    315     *
    316     * @param length if not strictly positive, the value is silently replaced by the default value (64).
    317     * @return this builder
    318     */
    319    public JexlBuilder cacheThreshold(final int length) {
    320        this.cacheThreshold = length > 0? length : CACHE_THRESHOLD;
    321        return this;
    322    }
    323
    324    /**
    325     * Gets the cancellable information flag
    326     * @return the cancellable information flag
    327     * @since 3.1
    328     */
    329    public Boolean cancellable() {
    330        return this.cancellable;
    331    }
    332
    333    /**
    334     * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation
    335     * and return null.
    336     *
    337     * @param flag true implies the engine throws the exception, false makes the engine return null.
    338     * @return this builder
    339     * @since 3.1
    340     */
    341    public JexlBuilder cancellable(final boolean flag) {
    342        this.cancellable = flag;
    343        options.setCancellable(flag);
    344        return this;
    345    }
    346
    347    /**
    348     * Gets the charset
    349     * @return the charset
    350     */
    351    public Charset charset() {
    352        return charset;
    353    }
    354
    355    /**
    356     * Sets the charset to use.
    357     *
    358     * @param arg the charset
    359     * @return this builder
    360     * @since 3.1
    361     */
    362    public JexlBuilder charset(final Charset arg) {
    363        this.charset = arg;
    364        return this;
    365    }
    366
    367    /**
    368     * Does the variable collection follow strict syntactic rule?
    369     * @return true if variable collection follows strict syntactic rule
    370     * @since 3.2
    371     */
    372    public boolean collectAll() {
    373        return this.collectMode != 0;
    374    }
    375
    376    /**
    377     * Sets whether the engine variable collectors considers all potential forms of variable syntaxes.
    378     *
    379     * @param flag true means var collections considers constant array accesses equivalent to dotted references
    380     * @return this builder
    381     * @since 3.2
    382     */
    383    public JexlBuilder collectAll(final boolean flag) {
    384        return collectMode(flag? 1 : 0);
    385    }
    386
    387    /**
    388     * Gets the collection mode.
    389     * @return 0 if variable collection follows strict syntactic rule
    390     * @since 3.2
    391     */
    392    public int collectMode() {
    393        return this.collectMode;
    394    }
    395
    396    /**
    397     * Experimental collector mode setter.
    398     *
    399     * @param mode 0 or 1 as equivalents to false and true, other values are experimental
    400     * @return this builder
    401     * @since 3.2
    402     */
    403    public JexlBuilder collectMode(final int mode) {
    404        this.collectMode = mode;
    405        return this;
    406    }
    407
    408    /**
    409     * Create a new engine
    410     * @return a {@link JexlEngine} instance
    411     */
    412    public JexlEngine create() {
    413        return new Engine(this);
    414    }
    415
    416    /**
    417     * Gets the debug flag
    418     * @return the debugging information flag
    419     */
    420    public Boolean debug() {
    421        return this.debug;
    422    }
    423
    424   /**
    425     * Sets whether the engine will report debugging information when error occurs.
    426     *
    427     * @param flag true implies debug is on, false implies debug is off.
    428     * @return this builder
    429     */
    430    public JexlBuilder debug(final boolean flag) {
    431        this.debug = flag;
    432        return this;
    433    }
    434
    435    /**
    436     * Gets the features the engine will use as a base by default.
    437     * @return the features
    438     */
    439    public JexlFeatures features() {
    440        return this.features;
    441    }
    442
    443    /**
    444     * Sets the features the engine will use as a base by default.
    445     * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts.
    446     * <p>Note also that these will apply to template expressions and scripts.
    447     * <p>As a last remark, if lexical or lexicalShade are set as features, this
    448     * method will also set the corresponding options.
    449     * @param f the features
    450     * @return this builder
    451     */
    452    public JexlBuilder features(final JexlFeatures f) {
    453        this.features = f;
    454        if (features != null) {
    455            if (features.isLexical()) {
    456                options.setLexical(true);
    457            }
    458            if (features.isLexicalShade()) {
    459                options.setLexicalShade(true);
    460            }
    461        }
    462        return this;
    463    }
    464
    465    /**
    466     * Gets the optional set of imported packages.
    467     * @return the set of imports, may be empty, not null
    468     */
    469    public Collection<String> imports() {
    470        return options.getImports();
    471    }
    472
    473    /**
    474     * Sets the optional set of imports.
    475     * @param imports the imported packages
    476     * @return this builder
    477     */
    478    public JexlBuilder imports(final Collection<String> imports) {
    479        options.setImports(imports);
    480        return this;
    481    }
    482
    483    /**
    484     * Sets the optional set of imports.
    485     * @param imports the imported packages
    486     * @return this builder
    487     */
    488    public JexlBuilder imports(final String... imports) {
    489        return imports(Arrays.asList(imports));
    490    }
    491
    492    /**
    493     * Is lexical scope enabled?
    494     * @see JexlOptions#isLexical()
    495     * @return whether lexical scope is enabled
    496     * @deprecated 3.5.0
    497     */
    498    @Deprecated
    499    public boolean lexical() {
    500        return options.isLexical();
    501    }
    502
    503    /**
    504     * Sets whether the engine is in lexical mode.
    505     *
    506     * @param flag true means lexical function scope is in effect, false implies non-lexical scoping
    507     * @return this builder
    508     * @since 3.2
    509     */
    510    public JexlBuilder lexical(final boolean flag) {
    511        options.setLexical(flag);
    512        return this;
    513    }
    514
    515    /**
    516     * Checks whether lexical shading is enabled.
    517     * @see JexlOptions#isLexicalShade()
    518     * @return whether lexical shading is enabled
    519     * @deprecated 3.5.0
    520     */
    521    @Deprecated
    522    public boolean lexicalShade() {
    523        return options.isLexicalShade();
    524    }
    525
    526    /**
    527     * Sets whether the engine is in lexical shading mode.
    528     *
    529     * @param flag true means lexical shading is in effect, false implies no lexical shading
    530     * @return this builder
    531     * @since 3.2
    532     */
    533    public JexlBuilder lexicalShade(final boolean flag) {
    534        options.setLexicalShade(flag);
    535        return this;
    536    }
    537
    538    /**
    539     * Gets the classloader
    540     * @return the class loader
    541     */
    542    public ClassLoader loader() {
    543        return loader;
    544    }
    545
    546    /**
    547     * Sets the charset to use.
    548     *
    549     * @param arg the charset
    550     * @return this builder
    551     * @deprecated since 3.1 use {@link #charset(Charset)} instead
    552     */
    553    @Deprecated
    554    public JexlBuilder loader(final Charset arg) {
    555        return charset(arg);
    556    }
    557
    558    /**
    559     * Sets the class loader to use.
    560     *
    561     * @param l the class loader
    562     * @return this builder
    563     */
    564    public JexlBuilder loader(final ClassLoader l) {
    565        this.loader = l;
    566        return this;
    567    }
    568
    569    /**
    570     * Gets the logger
    571     * @return the logger
    572     */
    573    public Log logger() {
    574        return this.logger;
    575    }
    576
    577    /**
    578     * Sets the o.a.c.Log instance to use.
    579     *
    580     * @param log the logger
    581     * @return this builder
    582     */
    583    public JexlBuilder logger(final Log log) {
    584        this.logger = log;
    585        return this;
    586    }
    587
    588    /**
    589     * Gets the map of namespaces.
    590     * @return the map of namespaces.
    591     */
    592    public Map<String, Object> namespaces() {
    593        return options.getNamespaces();
    594    }
    595
    596    /**
    597     * Sets the default namespaces map the engine will use.
    598     * <p>
    599     * Each entry key is used as a prefix, each entry value used as a bean implementing
    600     * methods; an expression like 'nsx:method(123)' will thus be solved by looking at
    601     * a registered bean named 'nsx' that implements method 'method' in that map.
    602     * If all methods are static, you may use the bean class instead of an instance as value.
    603     * </p>
    604     * <p>
    605     * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance
    606     * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext
    607     * to carry the information used by the namespace to avoid variable space pollution and strongly type
    608     * the constructor with this specialized JexlContext.
    609     * </p>
    610     * <p>
    611     * The key or prefix allows to retrieve the bean that plays the role of the namespace.
    612     * If the prefix is null, the namespace is the top-level namespace allowing to define
    613     * top-level user-defined namespaces ( ie: myfunc(...) )
    614     * </p>
    615     * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext
    616     * derived instances to call methods on the wrapped object.</p>
    617     *
    618     * @param ns the map of namespaces
    619     * @return this builder
    620     */
    621    public JexlBuilder namespaces(final Map<String, Object> ns) {
    622        options.setNamespaces(ns);
    623        return this;
    624    }
    625
    626    /**
    627     * Gets the current set of options
    628     * @return the current set of options
    629     */
    630    public JexlOptions options() {
    631      return options;
    632    }
    633
    634    /**
    635     * Gets the permissions
    636     * @return the permissions
    637     */
    638    public JexlPermissions permissions() {
    639        return this.permissions;
    640    }
    641
    642    /**
    643     * Sets the JexlPermissions instance the engine will use.
    644     *
    645     * @param p the permissions
    646     * @return this builder
    647     */
    648    public JexlBuilder permissions(final JexlPermissions p) {
    649        this.permissions = p;
    650        return this;
    651    }
    652
    653    /**
    654     * Is it safe to dereference null?
    655     * @return true if safe, false otherwise
    656     */
    657    public Boolean safe() {
    658        return options.isSafe();
    659    }
    660
    661    /**
    662     * Sets whether the engine considers dereferencing null in navigation expressions
    663     * as null or triggers an error.
    664     * <p>{@code x.y()} if x is null throws an exception when not safe,
    665     * return null and warns if it is.</p>
    666     * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p>
    667     *
    668     * @param flag true means safe navigation, false throws exception when dereferencing null
    669     * @return this builder
    670     */
    671    public JexlBuilder safe(final boolean flag) {
    672        options.setSafe(flag);
    673        return this;
    674    }
    675
    676    /**
    677     * Gets the sandbox
    678     * @return the sandbox
    679     */
    680    public JexlSandbox sandbox() {
    681        return this.sandbox;
    682    }
    683
    684    /**
    685     * Sets the sandbox the engine will use.
    686     *
    687     * @param box the sandbox
    688     * @return this builder
    689     */
    690    public JexlBuilder sandbox(final JexlSandbox box) {
    691        this.sandbox = box;
    692        return this;
    693    }
    694
    695    /**
    696     * Is error handling silent?
    697     * @return the silent error handling flag
    698     */
    699    public Boolean silent() {
    700        return options.isSilent();
    701    }
    702
    703    /**
    704     * Sets whether the engine will throw JexlException during evaluation when an error is triggered.
    705     * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an
    706     * error.</p>
    707     * <p>It is recommended to use <em>silent(true)</em> as an explicit default.</p>
    708     * @param flag true means no JexlException will occur, false allows them
    709     * @return this builder
    710     */
    711    public JexlBuilder silent(final boolean flag) {
    712        options.setSilent(flag);
    713        return this;
    714    }
    715
    716    /**
    717     * Gets the cache size
    718     * @return the cache size
    719     */
    720    public int stackOverflow() {
    721        return stackOverflow;
    722    }
    723
    724    /**
    725     * Sets the number of script/expression evaluations that can be stacked.
    726     * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown.
    727     * @return this builder
    728     */
    729    public JexlBuilder stackOverflow(final int size) {
    730        this.stackOverflow = size;
    731        return this;
    732    }
    733
    734    /**
    735     * Gets the JexlUberspect strategy
    736     * @return the JexlUberspect strategy */
    737    public JexlUberspect.ResolverStrategy strategy() {
    738        return this.strategy;
    739    }
    740
    741    /**
    742     * Sets the JexlUberspect strategy the engine will use.
    743     * <p>This is ignored if the uberspect has been set.
    744     *
    745     * @param rs the strategy
    746     * @return this builder
    747     */
    748    public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) {
    749        this.strategy = rs;
    750        return this;
    751    }
    752
    753    /**
    754     * Is it strict mode?
    755     * @return true if strict, false otherwise */
    756    public Boolean strict() {
    757        return options.isStrict();
    758    }
    759
    760    /**
    761     * Sets whether the engine considers unknown variables, methods, functions and constructors as errors or
    762     * evaluates them as null.
    763     * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When
    764     * strict, those raise exceptions.</p>
    765     * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p>
    766     *
    767     * @param flag true means strict error reporting, false allows them to be evaluated as null
    768     * @return this builder
    769     */
    770    public JexlBuilder strict(final boolean flag) {
    771        options.setStrict(flag);
    772        return this;
    773    }
    774
    775    /**
    776     * Is interpolation strict?
    777     * @see JexlOptions#setStrictInterpolation(boolean)
    778     * @param flag strict interpolation flag
    779     * @return this builder
    780     */
    781    public JexlBuilder strictInterpolation(final boolean flag) {
    782        options.setStrictInterpolation(flag);
    783        return this;
    784    }
    785
    786    /**
    787     * Gets the uberspect
    788     * @return the uberspect */
    789    public JexlUberspect uberspect() {
    790        return this.uberspect;
    791    }
    792
    793    /**
    794     * Sets the JexlUberspect instance the engine will use.
    795     *
    796     * @param u the uberspect
    797     * @return this builder
    798     */
    799    public JexlBuilder uberspect(final JexlUberspect u) {
    800        this.uberspect = u;
    801        return this;
    802    }
    803}