blob: 1fcfec496908bbd6b80297574b26e4b7e9cd873c [file] [log] [blame]
/*
* 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" &amp; "not-strict":
* <p> 0 &amp; 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" &amp; "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" &amp; "not-strict":
* <p>The error control grain is roughly on par with JEXL 1.0</p>
* </li>
* <li>When "not-silent" &amp; "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);
}
}