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