| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.jexl3; |
| |
| import org.apache.commons.jexl3.internal.Engine; |
| import org.apache.commons.jexl3.introspection.JexlSandbox; |
| import org.apache.commons.jexl3.introspection.JexlUberspect; |
| import org.apache.commons.logging.Log; |
| |
| import java.util.Map; |
| import java.nio.charset.Charset; |
| |
| /** |
| * Configure and builds a JexlEngine. |
| * |
| * <p>The <code>setSilent</code> and <code>setStrict</code> methods allow to fine-tune an engine instance behavior |
| * according to various error control needs. The strict flag tells the engine when and if null as operand is |
| * considered an error, the silent flag tells the engine what to do with the error |
| * (log as warning or throw exception).</p> |
| * |
| * <ul> |
| * <li>When "silent" & "not-strict": |
| * <p> 0 & null should be indicators of "default" values so that even in an case of error, |
| * something meaningful can still be inferred; may be convenient for configurations. |
| * </p> |
| * </li> |
| * <li>When "silent" & "strict": |
| * <p>One should probably consider using null as an error case - ie, every object |
| * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form |
| * can be used to workaround exceptional cases. |
| * Use case could be configuration with no implicit values or defaults. |
| * </p> |
| * </li> |
| * <li>When "not-silent" & "not-strict": |
| * <p>The error control grain is roughly on par with JEXL 1.0</p> |
| * </li> |
| * <li>When "not-silent" & "strict": |
| * <p>The finest error control grain is obtained; it is the closest to Java code - |
| * still augmented by "script" capabilities regarding automated conversions and type matching. |
| * </p> |
| * </li> |
| * </ul> |
| */ |
| public class JexlBuilder { |
| |
| /** The default maximum expression length to hit the expression cache. */ |
| protected static final int CACHE_THRESHOLD = 64; |
| |
| /** The JexlUberspect instance. */ |
| private JexlUberspect uberspect = null; |
| |
| /** The strategy strategy. */ |
| private JexlUberspect.ResolverStrategy strategy = null; |
| |
| /** The sandbox. */ |
| private JexlSandbox sandbox = null; |
| |
| /** The Log to which all JexlEngine messages will be logged. */ |
| private Log logger = null; |
| |
| /** |
| * Whether expressions evaluated by this engine will throw exceptions (false) or |
| * return null (true) on errors. Default is false. |
| */ |
| private Boolean silent = null; |
| |
| /** Whether this engine is in lenient or strict mode; if unspecified, use the arithmetic lenient property. */ |
| private Boolean strict = null; |
| |
| /** Whether error messages will carry debugging information. */ |
| private Boolean debug = null; |
| |
| /** Whether interrupt throws JexlException.Cancel. */ |
| private Boolean cancellable = null; |
| |
| /** The map of 'prefix:function' to object implementing the namespaces. */ |
| private Map<String, Object> namespaces = null; |
| |
| /** The {@link JexlArithmetic} instance. */ |
| private JexlArithmetic arithmetic = null; |
| |
| /** The cache size. */ |
| private int cache = -1; |
| |
| /** The maximum expression length to hit the expression cache. */ |
| private int cacheThreshold = CACHE_THRESHOLD; |
| |
| /** The charset. */ |
| private Charset charset = Charset.defaultCharset(); |
| |
| /** The class loader. */ |
| private ClassLoader loader = null; |
| |
| /** |
| * Sets the JexlUberspect instance the engine will use. |
| * |
| * @param u the uberspect |
| * @return this builder |
| */ |
| public JexlBuilder uberspect(JexlUberspect u) { |
| this.uberspect = u; |
| return this; |
| } |
| |
| /** @return the uberspect */ |
| public JexlUberspect uberspect() { |
| return this.uberspect; |
| } |
| |
| /** |
| * Sets the JexlUberspect strategy strategy the engine will use. |
| * <p>This is ignored if the uberspect has been set. |
| * |
| * @param rs the strategy |
| * @return this builder |
| */ |
| public JexlBuilder strategy(JexlUberspect.ResolverStrategy rs) { |
| this.strategy = rs; |
| return this; |
| } |
| |
| /** @return the strategy strategy */ |
| public JexlUberspect.ResolverStrategy strategy() { |
| return this.strategy; |
| } |
| |
| /** |
| * Sets the JexlArithmetic instance the engine will use. |
| * |
| * @param a the arithmetic |
| * @return this builder |
| */ |
| public JexlBuilder arithmetic(JexlArithmetic a) { |
| this.arithmetic = a; |
| return this; |
| } |
| |
| /** @return the arithmetic */ |
| public JexlArithmetic arithmetic() { |
| return this.arithmetic; |
| } |
| |
| /** |
| * Sets the sandbox the engine will use. |
| * |
| * @param box the sandbox |
| * @return this builder |
| */ |
| public JexlBuilder sandbox(JexlSandbox box) { |
| this.sandbox = box; |
| return this; |
| } |
| |
| /** @return the sandbox */ |
| public JexlSandbox sandbox() { |
| return this.sandbox; |
| } |
| |
| /** |
| * Sets the o.a.c.Log instance to use. |
| * |
| * @param l the logger |
| * @return this builder |
| */ |
| public JexlBuilder logger(Log l) { |
| this.logger = l; |
| return this; |
| } |
| |
| /** @return the logger */ |
| public Log logger() { |
| return this.logger; |
| } |
| |
| /** |
| * Sets the class loader to use. |
| * |
| * @param l the class loader |
| * @return this builder |
| */ |
| public JexlBuilder loader(ClassLoader l) { |
| this.loader = l; |
| return this; |
| } |
| |
| /** @return the class loader */ |
| public ClassLoader loader() { |
| return loader; |
| } |
| |
| /** |
| * Sets the charset to use. |
| * |
| * @param arg the charset |
| * @return this builder |
| */ |
| public JexlBuilder loader(Charset arg) { |
| this.charset = arg; |
| return this; |
| } |
| |
| /** @return the charset */ |
| public Charset charset() { |
| return charset; |
| } |
| |
| /** |
| * Sets whether the engine will throw JexlException during evaluation when an error is triggered. |
| * |
| * @param flag true means no JexlException will occur, false allows them |
| * @return this builder |
| */ |
| public JexlBuilder silent(boolean flag) { |
| this.silent = flag; |
| return this; |
| } |
| |
| /** @return the silent error handling flag */ |
| public Boolean silent() { |
| return this.silent; |
| } |
| |
| /** |
| * Sets whether the engine considers unknown variables, methods, functions and constructors as errors or |
| * evaluates them as null. |
| * |
| * @param flag true means strict error reporting, false allows them to be evaluated as null |
| * @return this builder |
| */ |
| public JexlBuilder strict(boolean flag) { |
| this.strict = flag; |
| return this; |
| } |
| |
| /** @return true if strict, false otherwise */ |
| public Boolean strict() { |
| return this.strict; |
| } |
| |
| /** |
| * Sets whether the engine will report debugging information when error occurs. |
| * |
| * @param flag true implies debug is on, false implies debug is off. |
| * @return this builder |
| */ |
| public JexlBuilder debug(boolean flag) { |
| this.debug = flag; |
| return this; |
| } |
| |
| /** @return the debugging information flag */ |
| public Boolean debug() { |
| return this.debug; |
| } |
| |
| /** |
| * Sets the engine behavior upon interruption: throw an JexlException.Cancel or terminates the current evaluation |
| * and return null. |
| * |
| * @param flag true implies the engine throws the exception, false makes the engine return null. |
| * @return this builder |
| */ |
| public JexlBuilder cancellable(boolean flag) { |
| this.cancellable = flag; |
| return this; |
| } |
| |
| /** @return the cancellable information flag */ |
| public Boolean cancellable() { |
| return this.cancellable; |
| } |
| |
| /** |
| * Sets the default namespaces map the engine will use. |
| * <p> |
| * Each entry key is used as a prefix, each entry value used as a bean implementing |
| * methods; an expression like 'nsx:method(123)' will thus be solved by looking at |
| * a registered bean named 'nsx' that implements method 'method' in that map. |
| * If all methods are static, you may use the bean class instead of an instance as value. |
| * </p> |
| * <p> |
| * If the entry value is a class that has one contructor taking a JexlContext as argument, an instance |
| * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext |
| * to carry the information used by the namespace to avoid variable space pollution and strongly type |
| * the constructor with this specialized JexlContext. |
| * </p> |
| * <p> |
| * The key or prefix allows to retrieve the bean that plays the role of the namespace. |
| * If the prefix is null, the namespace is the top-level namespace allowing to define |
| * top-level user defined namespaces ( ie: myfunc(...) ) |
| * </p> |
| * <p>Note that the JexlContext is also used to try to solve top-level namespaces. This allows ObjectContext |
| * derived instances to call methods on the wrapped object.</p> |
| * |
| * @param ns the map of namespaces |
| * @return this builder |
| */ |
| public JexlBuilder namespaces(Map<String, Object> ns) { |
| this.namespaces = ns; |
| return this; |
| } |
| |
| /** |
| * @return the map of namespaces. |
| */ |
| public Map<String, Object> namespaces() { |
| return this.namespaces; |
| } |
| |
| /** |
| * Sets the expression cache size the engine will use. |
| * <p>The cache will contain at most <code>size</code> expressions of at most <code>cacheThreshold</code> length. |
| * Note that all JEXL caches are held through SoftReferences and may be garbage-collected.</p> |
| * |
| * @param size if not strictly positive, no cache is used. |
| * @return this builder |
| */ |
| public JexlBuilder cache(int size) { |
| this.cache = size; |
| return this; |
| } |
| |
| /** |
| * @return the cache size |
| */ |
| public int cache() { |
| return cache; |
| } |
| |
| /** |
| * Sets the maximum length for an expression to be cached. |
| * <p>Expression whose length is greater than this expression cache length threshold will |
| * bypass the cache.</p> |
| * <p>It is expected that a "long" script will be parsed once and its reference kept |
| * around in user-space structures; the jexl expression cache has no added-value in this case.</p> |
| * |
| * @param length if not strictly positive, the value is silently replaced by the default value (64). |
| * @return this builder |
| */ |
| public JexlBuilder cacheThreshold(int length) { |
| this.cacheThreshold = length > 0? length : CACHE_THRESHOLD; |
| return this; |
| } |
| |
| /** |
| * @return the cache threshold |
| */ |
| public int cacheThreshold() { |
| return cacheThreshold; |
| } |
| |
| /** |
| * @return a {@link JexlEngine} instance |
| */ |
| public JexlEngine create() { |
| return new Engine(this); |
| } |
| } |