Source code
Description: source: package: org.apache.commons.jexl3, class: JexlBuilder

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.jexl3;
    019
    020import java.nio.charset.Charset;
    021import java.util.Arrays;
    022import java.util.Collection;
    023import java.util.Map;
    024import java.util.function.IntFunction;
    025import java.util.function.Supplier;
    026
    027import org.apache.commons.jexl3.internal.Engine;
    028import org.apache.commons.jexl3.internal.SoftCache;
    029import org.apache.commons.jexl3.introspection.JexlPermissions;
    030import org.apache.commons.jexl3.introspection.JexlSandbox;
    031import org.apache.commons.jexl3.introspection.JexlUberspect;
    032import org.apache.commons.jexl3.parser.JexlScriptParser;
    033import org.apache.commons.logging.Log;
    034
    035/**
    036 * Configures and builds a JexlEngine.
    037 *
    038 * <p>
    039 *     The builder allow fine-tuning an engine instance behavior according to various control needs.
    040 *     Check <em>{@link #JexlBuilder()}</em> for permission impacts starting with <em>JEXL 3.3</em>.
    041 * </p><p>
    042 *     Broad configurations elements are controlled through the features ({@link JexlFeatures}) that can restrict JEXL
    043 *  syntax - for instance, only expressions with no-side effects - and permissions ({@link JexlPermissions}) that control
    044 *  the visible set of objects - for instance, avoiding access to any object in java.rmi.* -.
    045 *  </p><p>
    046 *     Fine error control and runtime-overridable behaviors are implemented through options ({@link JexlOptions}). Most
    047 * common flags accessible from the builder are reflected in its options ({@link #options()}).
    048 * </p><p>
    049 *     The {@code silent} flag tells the engine what to do with the error; when true, errors are logged as
    050 * warning, when false, they throw {@link JexlException} exceptions.
    051 * </p><p>
    052 *     The {@code strict} flag tells the engine when and if null as operand is considered an error. The {@code safe}
    053 * flog determines if safe-navigation is used. Safe-navigation allows an  evaluation shortcut and return null in expressions
    054 * that attempts dereferencing null, typically a method call or accessing a property.
    055 * </p><p>
    056 *     The {@code lexical} and {@code lexicalShade} flags can be used to enforce a lexical scope for
    057 * variables and parameters. The {@code lexicalShade} can be used to further ensure no global variable can be
    058 * used with the same name as a local one even after it goes out of scope. The corresponding feature flags should be
    059 * preferred since they will detect violations at parsing time. (see {@link JexlFeatures})
    060 * </p><p>
    061 *     The following rules apply on silent and strict flags:
    062 * </p>
    063 * <ul>
    064 * <li>When "silent" &amp; "not-strict":
    065 * <p> 0 &amp; null should be indicators of "default" values so that even in an case of error,
    066 * something meaningful can still be inferred; may be convenient for configurations.
    067 * </p>
    068 * </li>
    069 * <li>When "silent" &amp; "strict":
    070 * <p>One should probably consider using null as an error case - ie, every object
    071 * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
    072 * can be used to workaround exceptional cases.
    073 * Use case could be configuration with no implicit values or defaults.
    074 * </p>
    075 * </li>
    076 * <li>When "not-silent" &amp; "not-strict":
    077 * <p>The error control grain is roughly on par with JEXL 1.0</p>
    078 * </li>
    079 * <li>When "not-silent" &amp; "strict":
    080 * <p>The finest error control grain is obtained; it is the closest to Java code -
    081 * still augmented by "script" capabilities regarding automated conversions and type matching.
    082 * </p>
    083 * </li>
    084 * </ul>
    085 */
    086public class JexlBuilder {
    087
    088    /**
    089     * The set of default permissions used when creating a new builder.
    090     * <p>Static but modifiable, so these default permissions can be changed to a purposeful set.</p>
    091     * <p>In JEXL 3.7, these are {@link JexlPermissions#SECURE}.</p>
    092     * <p>In JEXL 3.3, these were {@link JexlPermissions#RESTRICTED}.</p>
    093     * <p>In JEXL 3.2, these were equivalent to {@link JexlPermissions#UNRESTRICTED}.</p>
    094     */
    095    private static JexlPermissions PERMISSIONS = JexlPermissions.SECURE;
    096
    097    /**
    098     * The full feature set that was the implicit default before JEXL 3.7.
    099     * <p>This captures {@link JexlFeatures#createDefault()} - the feature set the engine used as its
    100     * fallback throughout 3.x (since 3.3) before the 3.7 hardening. It enables {@code new(...)}, loops,
    101     * side-effects, lambdas, method calls, structured literals, pragmas, etc. Use it (or
    102     * {@link #setDefaultFeatures(JexlFeatures)}) to restore pre-3.7 behavior:</p>
    103     * <pre>
    104     * JexlEngine jexl = new JexlBuilder().features(JexlBuilder.FULL).create();
    105     * </pre>
    106     * <p>{@link JexlFeatures} instances are mutable; copy this one via {@code new JexlFeatures(JexlBuilder.FULL)}
    107     * before customizing it rather than mutating the shared instance.</p>
    108     *
    109     * @since 3.7.0
    110     */
    111    public static final JexlFeatures FULL = JexlFeatures.createDefault();
    112
    113    /**
    114     * The set of default features used when creating a new builder.
    115     * <p>Static but modifiable via {@link #setDefaultFeatures(JexlFeatures)}.</p>
    116     * <p>In JEXL 3.7, the default hardens the feature set relative to {@link #FULL}: {@code new(...)},
    117     * global side-effects, pragmas, and annotations are disabled, and lexical scoping (with shade) is
    118     * enabled.  Loops remain available to scripts (expressions never allow them).  Scripts using a
    119     * disabled construct will throw {@link JexlException.Feature} at parse time.</p>
    120     *
    121     * @see #secureFeatures()
    122     */
    123    private static JexlFeatures FEATURES = secureFeatures();
    124
    125    /** The default maximum expression length to hit the expression cache. */
    126    protected static final int CACHE_THRESHOLD = 64;
    127
    128    /**
    129     * Sets the default permissions.
    130     *
    131     * @param permissions the permissions
    132     */
    133    public static void setDefaultPermissions(final JexlPermissions permissions) {
    134        PERMISSIONS = permissions == null ? JexlPermissions.SECURE : permissions;
    135    }
    136
    137    /**
    138     * Sets the default features used when creating a new builder.
    139     * <p>Passing {@code null} restores the 3.7 hardened default (new-instance, global side-effects,
    140     * pragmas, and annotations disabled, lexical scoping enabled; see {@link #secureFeatures()}).</p>
    141     *
    142     * @param features the features, or {@code null} to restore the hardened default
    143     * @since 3.7.0
    144     */
    145    public static void setDefaultFeatures(final JexlFeatures features) {
    146        FEATURES = features == null ? secureFeatures() : features;
    147    }
    148
    149    /**
    150     * Sets the default option flags used when creating a new engine.
    151     * <p>Delegates to {@link JexlOptions#setDefaultFlags(String...)}; see that method for the
    152     * flag-name syntax ({@code "+flag"}/{@code "-flag"}) and the available flag names
    153     * ({@code cancellable}, {@code strict}, {@code silent}, {@code safe}, {@code lexical},
    154     * {@code antish}, {@code lexicalShade}, ...).</p>
    155     * <p>Example: restore safe/antish defaults before creating any engine:</p>
    156     * <pre>JexlBuilder.setDefaultOptions("+safe", "+antish");</pre>
    157     *
    158     * @param flags the flags to set or clear
    159     * @since 3.7.0
    160     */
    161    public static void setDefaultOptions(final String... flags) {
    162        JexlOptions.setDefaultFlags(flags);
    163    }
    164
    165    /**
    166     * Builds the JEXL 3.7 hardened default feature set.
    167     * <p>Starting from {@link #FULL} (the pre-3.7 feature set), this disables {@code new(...)},
    168     * global side-effects, pragmas, and annotations, and enables lexical scoping and lexical shade.
    169     * Loops are left enabled so scripts can iterate; expressions never allow loops since they are
    170     * parsed as a single expression.</p>
    171     *
    172     * @return a fresh hardened feature set
    173     * @since 3.7.0
    174     */
    175    private static JexlFeatures secureFeatures() {
    176        return new JexlFeatures(FULL)
    177            .newInstance(false)
    178            .sideEffectGlobal(false)
    179            .pragma(false)
    180            .annotation(false)
    181            .loops(true)
    182            .lexical(true)
    183            .lexicalShade(true);
    184    }
    185
    186    /** The JexlUberspect instance. */
    187    private JexlUberspect uberspect;
    188
    189    /** The {@link JexlUberspect} resolver strategy. */
    190    private JexlUberspect.ResolverStrategy strategy;
    191
    192    /** The set of permissions. */
    193    private JexlPermissions permissions;
    194
    195    /** The sandbox. */
    196    private JexlSandbox sandbox;
    197
    198    /** The Log to which all JexlEngine messages will be logged. */
    199    private Log logger;
    200
    201    /** Whether error messages will carry debugging information. */
    202    private Boolean debug;
    203
    204    /** Whether interrupt throws JexlException.Cancel. */
    205    private Boolean cancellable;
    206
    207    /** The options. */
    208    private final JexlOptions options = new JexlOptions();
    209
    210    /** Whether getVariables considers all potential equivalent syntactic forms. */
    211    private int collectMode = 1;
    212
    213    /** The {@link JexlArithmetic} instance. */
    214    private JexlArithmetic arithmetic;
    215
    216    /** The cache size. */
    217    private int cache = -1;
    218
    219    /** The cache class factory. */
    220    private IntFunction<JexlCache<?,?>> cacheFactory = SoftCache::new;
    221
    222   /** The parser class factory. */
    223   private Supplier<JexlScriptParser> parserFactory;
    224
    225    /** The stack overflow limit. */
    226    private int stackOverflow = Integer.MAX_VALUE;
    227
    228    /** The maximum expression length to hit the expression cache. */
    229    private int cacheThreshold = CACHE_THRESHOLD;
    230
    231    /** The charset. */
    232    private Charset charset = Charset.defaultCharset();
    233
    234    /** The class loader. */
    235    private ClassLoader loader;
    236
    237    /** The features. */
    238    private JexlFeatures features;
    239
    240    /**
    241     * Default constructor.
    242     * <p>
    243     * As of JEXL 3.3, to reduce the security risks inherent to JEXL&quot;s purpose, the builder will use a set of
    244     * restricted permissions as a default to create the {@link JexlEngine} instance. This will greatly reduce which classes
    245     * and methods are visible to JEXL and usable in scripts using default implicit behaviors.
    246     * </p><p>
    247     * However, without mitigation, this change will likely break some scripts at runtime, especially those exposing
    248     * your own class instances through arguments, contexts, or namespaces.
    249     * The default set of allowed packages and denied classes is described by {@link JexlPermissions#SECURE}
    250     * as of JEXL 3.7 (previously {@link JexlPermissions#RESTRICTED} since 3.3).
    251     * </p><p>
    252     * As of JEXL 3.7, the default features also disable {@code new(...)}, global side-effects, pragmas,
    253     * and annotations, and enable lexical scoping; loops remain available to scripts (but never to
    254     * expressions). Scripts using a disabled construct will throw {@link JexlException.Feature} at parse
    255     * time unless a permissive feature set is configured.
    256     * </p><p>
    257     * The recommended mitigation if your usage of JEXL is impacted is to load a {@code jexl.yaml}
    258     * configuration via {@link JexlConfigLoader} that restores the legacy permissions and features,
    259     * or to explicitly configure them via {@link #permissions(JexlPermissions)} and
    260     * {@link #features(JexlFeatures)}. Use {@link JexlPermissions#logging()} to discover which
    261     * reflective elements the new algorithm denies.
    262     * </p><p>
    263     * Note that an explicit call to {@link #uberspect(JexlUberspect)} will supersede any permissions related behavior
    264     * by using the {@link JexlUberspect} provided as argument used as-is in the created {@link JexlEngine}.
    265     * </p>
    266     *
    267     * @since 3.3
    268     */
    269    public JexlBuilder() {
    270        this.permissions = PERMISSIONS;
    271        this.features = FEATURES == null ? null : new JexlFeatures(FEATURES);
    272    }
    273
    274    /**
    275     * Is antish resolution enabled?
    276     *
    277     * @return whether antish resolution is enabled
    278     */
    279    public boolean antish() {
    280        return options.isAntish();
    281    }
    282
    283    /**
    284     * Sets whether the engine will resolve antish variable names.
    285     *
    286     * @param flag true means antish resolution is enabled, false disables it
    287     * @return this builder
    288     */
    289    public JexlBuilder antish(final boolean flag) {
    290        options.setAntish(flag);
    291        return this;
    292    }
    293
    294    /**
    295     * Gets the JexlArithmetic instance the engine will use.
    296     *
    297     * @return the arithmetic
    298     */
    299    public JexlArithmetic arithmetic() {
    300        return this.arithmetic;
    301    }
    302
    303    /**
    304     * Sets the JexlArithmetic instance the engine will use.
    305     *
    306     * @param a the arithmetic
    307     * @return this builder
    308     */
    309    public JexlBuilder arithmetic(final JexlArithmetic a) {
    310        this.arithmetic = a;
    311        options.setStrictArithmetic(a.isStrict());
    312        options.setMathContext(a.getMathContext());
    313        options.setMathScale(a.getMathScale());
    314        return this;
    315    }
    316
    317    /**
    318     * Sets whether logical expressions (&quot;&quot; , ||) coerce their result to boolean.
    319     *
    320     * @param flag true or false
    321     * @return this builder
    322     */
    323    public JexlBuilder booleanLogical(final boolean flag) {
    324      options.setBooleanLogical(flag);
    325      return this;
    326    }
    327
    328    /**
    329     * Gets the expression cache size the engine will use.
    330     *
    331     * @return the cache size
    332     */
    333    public int cache() {
    334      return cache;
    335    }
    336
    337    /**
    338     * Sets the expression cache size the engine will use.
    339     * <p>The cache will contain at most {@code size} expressions of at most {@code cacheThreshold} length.
    340     * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p>
    341     *
    342     * @param size if not strictly positive, no cache is used.
    343     * @return this builder
    344     */
    345    public JexlBuilder cache(final int size) {
    346        this.cache = size;
    347        return this;
    348    }
    349
    350    /**
    351     * Gets the expression-cache factory the engine will use.
    352     *
    353     * @return the cache factory
    354     */
    355    public IntFunction<JexlCache<?, ?>> cacheFactory() {
    356      return this.cacheFactory;
    357    }
    358
    359    /**
    360     * Sets the expression-cache factory the engine will use.
    361     *
    362     * @param factory the function to produce a cache.
    363     * @return this builder
    364     */
    365    public JexlBuilder cacheFactory(final IntFunction<JexlCache<?, ?>> factory) {
    366      this.cacheFactory = factory;
    367      return this;
    368    }
    369
    370  /**
    371   * Gets the Jexl script parser factory the engine will use.
    372   *
    373   * @return the cache factory
    374   * @since 3.5.0
    375   */
    376  public Supplier<JexlScriptParser> parserFactory() {
    377    return this.parserFactory;
    378  }
    379
    380  /**
    381   * Sets the Jexl script parser factory the engine will use.
    382   *
    383   * @param factory the function to produce a cache.
    384   * @return this builder
    385   * @since 3.5.0
    386   */
    387    public JexlBuilder parserFactory(final Supplier<JexlScriptParser> factory) {
    388      this.parserFactory = factory;
    389      return this;
    390    }
    391
    392    /**
    393     * Gets the maximum length for an expression to be cached.
    394     *
    395     * @return the cache threshold
    396     */
    397    public int cacheThreshold() {
    398        return cacheThreshold;
    399    }
    400
    401    /**
    402     * Sets the maximum length for an expression to be cached.
    403     * <p>Expression whose length is greater than this expression cache length threshold will
    404     * bypass the cache.</p>
    405     * <p>It is expected that a "long" script will be parsed once and its reference kept
    406     * around in user-space structures; the jexl expression cache has no added-value in this case.</p>
    407     *
    408     * @param length if not strictly positive, the value is silently replaced by the default value (64).
    409     * @return this builder
    410     */
    411    public JexlBuilder cacheThreshold(final int length) {
    412        this.cacheThreshold = length > 0? length : CACHE_THRESHOLD;
    413        return this;
    414    }
    415
    416    /**
    417     * Gets the cancellable information flag
    418     *
    419     * @return the cancellable information flag
    420     * @since 3.1
    421     */
    422    public Boolean cancellable() {
    423        return this.cancellable;
    424    }
    425
    426    /**
    427     * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation
    428     * and return null.
    429     *
    430     * @param flag true implies the engine throws the exception, false makes the engine return null.
    431     * @return this builder
    432     * @since 3.1
    433     */
    434    public JexlBuilder cancellable(final boolean flag) {
    435        this.cancellable = flag;
    436        options.setCancellable(flag);
    437        return this;
    438    }
    439
    440    /**
    441     * Gets the charset
    442     *
    443     * @return the charset
    444     */
    445    public Charset charset() {
    446        return charset;
    447    }
    448
    449    /**
    450     * Sets the charset to use.
    451     *
    452     * @param arg the charset
    453     * @return this builder
    454     * @since 3.1
    455     */
    456    public JexlBuilder charset(final Charset arg) {
    457        this.charset = arg;
    458        return this;
    459    }
    460
    461    /**
    462     * Does the variable collection follow strict syntactic rule?
    463     *
    464     * @return true if variable collection follows strict syntactic rule
    465     * @since 3.2
    466     */
    467    public boolean collectAll() {
    468        return this.collectMode != 0;
    469    }
    470
    471    /**
    472     * Sets whether the engine variable collectors considers all potential forms of variable syntaxes.
    473     *
    474     * @param flag true means var collections considers constant array accesses equivalent to dotted references
    475     * @return this builder
    476     * @since 3.2
    477     */
    478    public JexlBuilder collectAll(final boolean flag) {
    479        return collectMode(flag? 1 : 0);
    480    }
    481
    482    /**
    483     * Gets the collection mode.
    484     *
    485     * @return 0 if variable collection follows strict syntactic rule
    486     * @since 3.2
    487     */
    488    public int collectMode() {
    489        return this.collectMode;
    490    }
    491
    492    /**
    493     * Experimental collector mode setter.
    494     *
    495     * @param mode 0 or 1 as equivalents to false and true, other values are experimental
    496     * @return this builder
    497     * @since 3.2
    498     */
    499    public JexlBuilder collectMode(final int mode) {
    500        this.collectMode = mode;
    501        return this;
    502    }
    503
    504    /**
    505     * Create a new engine
    506     *
    507     * @return a {@link JexlEngine} instance
    508     */
    509    public JexlEngine create() {
    510        return new Engine(this);
    511    }
    512
    513    /**
    514     * Gets the debug flag.
    515     *
    516     * @return the debugging information flag
    517     */
    518    public Boolean debug() {
    519        return this.debug;
    520    }
    521
    522    /**
    523     * Sets whether the engine will report debugging information when error occurs.
    524     *
    525     * @see JexlEngine#isDebug()
    526     * @param flag true implies debug is on, false implies debug is off.
    527     * @return this builder
    528     */
    529    public JexlBuilder debug(final boolean flag) {
    530        this.debug = flag;
    531        return this;
    532    }
    533
    534    /**
    535     * Gets the features the engine will use as a base by default.
    536     *
    537     * @return the features
    538     */
    539    public JexlFeatures features() {
    540        return this.features;
    541    }
    542
    543    /**
    544     * Sets the features the engine will use as a base by default.
    545     * <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts.
    546     * <p>Note also that these will apply to template expressions and scripts.
    547     * <p>As a last remark, if lexical or lexicalShade are set as features, this
    548     * method will also set the corresponding options.
    549     *
    550     * @param f the features
    551     * @return this builder
    552     */
    553    public JexlBuilder features(final JexlFeatures f) {
    554        this.features = f;
    555        if (features != null) {
    556            if (features.isLexical()) {
    557                options.setLexical(true);
    558            }
    559            if (features.isLexicalShade()) {
    560                options.setLexicalShade(true);
    561            }
    562        }
    563        return this;
    564    }
    565
    566    /**
    567     * Gets the optional set of imported packages.
    568     *
    569     * @return the set of imports, may be empty, not null
    570     */
    571    public Collection<String> imports() {
    572        return options.getImports();
    573    }
    574
    575    /**
    576     * Sets the optional set of imports.
    577     *
    578     * @param imports the imported packages
    579     * @return this builder
    580     */
    581    public JexlBuilder imports(final Collection<String> imports) {
    582        options.setImports(imports);
    583        return this;
    584    }
    585
    586    /**
    587     * Sets the optional set of imports.
    588     *
    589     * @param imports the imported packages
    590     * @return this builder
    591     */
    592    public JexlBuilder imports(final String... imports) {
    593        return imports(Arrays.asList(imports));
    594    }
    595
    596    /**
    597     * Is lexical scope enabled?
    598     *
    599     * @see JexlOptions#isLexical()
    600     * @return whether lexical scope is enabled
    601     * @deprecated 3.5.0
    602     */
    603    @Deprecated
    604    public boolean lexical() {
    605        return options.isLexical();
    606    }
    607
    608    /**
    609     * Sets whether the engine is in lexical mode.
    610     *
    611     * @param flag true means lexical function scope is in effect, false implies non-lexical scoping
    612     * @return this builder
    613     * @since 3.2
    614     */
    615    public JexlBuilder lexical(final boolean flag) {
    616        options.setLexical(flag);
    617        return this;
    618    }
    619
    620    /**
    621     * Checks whether lexical shading is enabled.
    622     *
    623     * @see JexlOptions#isLexicalShade()
    624     * @return whether lexical shading is enabled
    625     * @deprecated 3.5.0
    626     */
    627    @Deprecated
    628    public boolean lexicalShade() {
    629        return options.isLexicalShade();
    630    }
    631
    632    /**
    633     * Sets whether the engine is in lexical shading mode.
    634     *
    635     * @param flag true means lexical shading is in effect; false implies no lexical shading
    636     * @return this builder
    637     * @since 3.2
    638     */
    639    public JexlBuilder lexicalShade(final boolean flag) {
    640        options.setLexicalShade(flag);
    641        return this;
    642    }
    643
    644    /**
    645     * Gets the classloader
    646     *
    647     * @return the class loader
    648     */
    649    public ClassLoader loader() {
    650        return loader;
    651    }
    652
    653    /**
    654     * Sets the charset to use.
    655     *
    656     * @param arg the charset
    657     * @return this builder
    658     * @deprecated since 3.1 use {@link #charset(Charset)} instead
    659     */
    660    @Deprecated
    661    public JexlBuilder loader(final Charset arg) {
    662        return charset(arg);
    663    }
    664
    665    /**
    666     * Sets the class loader to use.
    667     *
    668     * @param l the class loader
    669     * @return this builder
    670     */
    671    public JexlBuilder loader(final ClassLoader l) {
    672        this.loader = l;
    673        return this;
    674    }
    675
    676    /**
    677     * Gets the logger
    678     *
    679     * @return the logger
    680     */
    681    public Log logger() {
    682        return this.logger;
    683    }
    684
    685    /**
    686     * Sets the o.a.c.Log instance to use.
    687     *
    688     * @param log the logger
    689     * @return this builder
    690     */
    691    public JexlBuilder logger(final Log log) {
    692        this.logger = log;
    693        return this;
    694    }
    695
    696    /**
    697     * Gets the map of namespaces.
    698     *
    699     * @return the map of namespaces.
    700     */
    701    public Map<String, Object> namespaces() {
    702        return options.getNamespaces();
    703    }
    704
    705    /**
    706     * Sets the default namespaces map the engine will use.
    707     * <p>
    708     * Each entry key is used as a prefix, each entry value used as a bean implementing
    709     * methods; an expression like 'nsx:method(123)' will thus be solved by looking at
    710     * a registered bean named 'nsx' that implements method 'method' in that map.
    711     * If all methods are static, you may use the bean class instead of an instance as value.
    712     * </p>
    713     * <p>
    714     * If the entry value is a class that has one constructor taking a JexlContext as argument, an instance
    715     * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext
    716     * to carry the information used by the namespace to avoid variable space pollution and strongly type
    717     * the constructor with this specialized JexlContext.
    718     * </p>
    719     * <p>
    720     * The key or prefix allows to retrieve the bean that plays the role of the namespace.
    721     * If the prefix is null, the namespace is the top-level namespace allowing to define
    722     * top-level user-defined namespaces ( ie: myfunc(...) )
    723     * </p>
    724     * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext
    725     * derived instances to call methods on the wrapped object.</p>
    726     *
    727     * @param ns the map of namespaces
    728     * @return this builder
    729     */
    730    public JexlBuilder namespaces(final Map<String, Object> ns) {
    731        options.setNamespaces(ns);
    732        return this;
    733    }
    734
    735    /**
    736     * Gets the current set of options
    737     *
    738     * @return the current set of options
    739     */
    740    public JexlOptions options() {
    741      return options;
    742    }
    743
    744    /**
    745     * Gets the permissions
    746     *
    747     * @return the permissions
    748     */
    749    public JexlPermissions permissions() {
    750        return this.permissions;
    751    }
    752
    753    /**
    754     * Sets the JexlPermissions instance the engine will use.
    755     *
    756     * @param p the permissions
    757     * @return this builder
    758     */
    759    public JexlBuilder permissions(final JexlPermissions p) {
    760        this.permissions = p;
    761        return this;
    762    }
    763
    764    /**
    765     * Is it safe to dereference null?
    766     *
    767     * @return true if safe, false otherwise
    768     */
    769    public Boolean safe() {
    770        return options.isSafe();
    771    }
    772
    773    /**
    774     * Sets whether the engine considers dereferencing null in navigation expressions
    775     * as null or triggers an error.
    776     * <p>{@code x.y()} if x is null throws an exception when not safe,
    777     * return null and warns if it is.</p>
    778     * <p>It is recommended to use <em>safe(false)</em> as an explicit default.</p>
    779     *
    780     * @param flag true means safe navigation, false throws exception when dereferencing null
    781     * @return this builder
    782     */
    783    public JexlBuilder safe(final boolean flag) {
    784        options.setSafe(flag);
    785        return this;
    786    }
    787
    788    /**
    789     * Gets the sandbox
    790     *
    791     * @return the sandbox
    792     */
    793    public JexlSandbox sandbox() {
    794        return this.sandbox;
    795    }
    796
    797    /**
    798     * Sets the sandbox the engine will use.
    799     *
    800     * @param box the sandbox
    801     * @return this builder
    802     */
    803    public JexlBuilder sandbox(final JexlSandbox box) {
    804        this.sandbox = box;
    805        return this;
    806    }
    807
    808    /**
    809     * Is error handling silent?
    810     *
    811     * @return the silent error handling flag
    812     */
    813    public Boolean silent() {
    814        return options.isSilent();
    815    }
    816
    817    /**
    818     * Sets whether the engine will throw JexlException during evaluation when an error is triggered.
    819     * <p>When <em>not</em> silent, the engine throws an exception when the evaluation triggers an exception or an
    820     * error.</p>
    821     * <p>It is recommended to use <em>silent(false)</em> as an explicit default.</p>
    822     *
    823     * @param flag true means no JexlException will occur, false allows them
    824     * @return this builder
    825     */
    826    public JexlBuilder silent(final boolean flag) {
    827        options.setSilent(flag);
    828        return this;
    829    }
    830
    831    /**
    832     * Gets the cache size
    833     *
    834     * @return the cache size
    835     */
    836    public int stackOverflow() {
    837        return stackOverflow;
    838    }
    839
    840    /**
    841     * Sets the number of script/expression evaluations that can be stacked.
    842     *
    843     * @param size if not strictly positive, limit is reached when Java StackOverflow is thrown.
    844     * @return this builder
    845     */
    846    public JexlBuilder stackOverflow(final int size) {
    847        this.stackOverflow = size;
    848        return this;
    849    }
    850
    851    /**
    852     * Gets the JexlUberspect strategy
    853     *
    854     * @return the JexlUberspect strategy */
    855    public JexlUberspect.ResolverStrategy strategy() {
    856        return this.strategy;
    857    }
    858
    859    /**
    860     * Sets the JexlUberspect strategy the engine will use.
    861     * <p>This is ignored if the uberspect has been set.
    862     *
    863     * @param rs the strategy
    864     * @return this builder
    865     */
    866    public JexlBuilder strategy(final JexlUberspect.ResolverStrategy rs) {
    867        this.strategy = rs;
    868        return this;
    869    }
    870
    871    /**
    872     * Is it strict mode?
    873     *
    874     * @return true if strict, false otherwise */
    875    public Boolean strict() {
    876        return options.isStrict();
    877    }
    878
    879    /**
    880     * Sets whether the engine considers unknown variables, methods, functions, and constructors as errors or
    881     * evaluates them as null.
    882     * <p>When <em>not</em> strict, operators or functions using null operands return null on evaluation. When
    883     * strict, those raise exceptions.</p>
    884     * <p>It is recommended to use <em>strict(true)</em> as an explicit default.</p>
    885     *
    886     * @param flag true means strict error reporting, false allows them to be evaluated as null
    887     * @return this builder
    888     */
    889    public JexlBuilder strict(final boolean flag) {
    890        options.setStrict(flag);
    891        return this;
    892    }
    893
    894    /**
    895     * Is interpolation strict?
    896     *
    897     * @see JexlOptions#setStrictInterpolation(boolean)
    898     * @param flag strict interpolation flag
    899     * @return this builder
    900     */
    901    public JexlBuilder strictInterpolation(final boolean flag) {
    902        options.setStrictInterpolation(flag);
    903        return this;
    904    }
    905
    906    /**
    907     * Gets the uberspect
    908     *
    909     * @return the uberspect */
    910    public JexlUberspect uberspect() {
    911        return this.uberspect;
    912    }
    913
    914    /**
    915     * Sets the JexlUberspect instance the engine will use.
    916     *
    917     * @param u the uberspect
    918     * @return this builder
    919     */
    920    public JexlBuilder uberspect(final JexlUberspect u) {
    921        this.uberspect = u;
    922        return this;
    923    }
    924}