blob: 62666b9ba6ae13723ab71d5c8bb1a20040c10253 [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 java.math.MathContext;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.jexl3.internal.Engine;
/**
* Flags and properties that can alter the evaluation behavior.
* The flags, briefly explained, are the following:
* <ul>
* <li>silent: whether errors throw exception</li>
* <li>safe: whether navigation through null is an error</li>
* <li>cancellable: whether thread interruption is an error</li>
* <li>lexical: whether redefining local variables is an error</li>
* <li>lexicalShade: whether local variables shade global ones even outside their scope</li>
* <li>strict: whether unknown or unsolvable identifiers are errors</li>
* <li>strictArithmetic: whether null as operand is an error</li>
* <li>sharedInstance: whether these options can be modified at runtime during execution (expert)</li>
* </ul>
* The sensible default is cancellable, strict and strictArithmetic.
* <p>This interface replaces the now deprecated JexlEngine.Options.
* @since 3.2
*/
public final class JexlOptions {
/** The shared instance bit. */
private static final int SHARED = 7;
/** The local shade bit. */
private static final int SHADE = 6;
/** The antish var bit. */
private static final int ANTISH = 5;
/** The lexical scope bit. */
private static final int LEXICAL = 4;
/** The safe bit. */
private static final int SAFE = 3;
/** The silent bit. */
private static final int SILENT = 2;
/** The strict bit. */
private static final int STRICT = 1;
/** The cancellable bit. */
private static final int CANCELLABLE = 0;
/** The flags names ordered. */
private static final String[] NAMES = {
"cancellable", "strict", "silent", "safe", "lexical", "antish", "lexicalShade", "sharedInstance"
};
/** Default mask .*/
private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH | 1 << SAFE;
/** The arithmetic math context. */
private MathContext mathContext = null;
/** The arithmetic math scale. */
private int mathScale = Integer.MIN_VALUE;
/** The arithmetic strict math flag. */
private boolean strictArithmetic = true;
/** The default flags, all but safe. */
private int flags = DEFAULT;
/** The namespaces .*/
private Map<String, Object> namespaces = Collections.emptyMap();
/**
* Sets the value of a flag in a mask.
* @param ordinal the flag ordinal
* @param mask the flags mask
* @param value true or false
* @return the new flags mask value
*/
private static int set(final int ordinal, final int mask, final boolean value) {
return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
}
/**
* Checks the value of a flag in the mask.
* @param ordinal the flag ordinal
* @param mask the flags mask
* @return the mask value with this flag or-ed in
*/
private static boolean isSet(final int ordinal, final int mask) {
return (mask & 1 << ordinal) != 0;
}
/**
* Default ctor.
*/
public JexlOptions() {}
/**
* Sets the default (static, shared) option flags.
* <p>
* Whenever possible, we recommend using JexlBuilder methods to unambiguously instantiate a JEXL
* engine; this method should only be used for testing / validation.
* <p>A '+flag' or 'flag' will set the option named 'flag' as true, '-flag' set as false.
* The possible flag names are:
* cancellable, strict, silent, safe, lexical, antish, lexicalShade
* <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL engine creation
* may ease validating JEXL3.2 in your environment.
* @param flags the flags to set
*/
public static void setDefaultFlags(final String...flags) {
DEFAULT = parseFlags(DEFAULT, flags);
}
/**
* Parses flags by name.
* <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
* The possible flag names are:
* cancellable, strict, silent, safe, lexical, antish, lexicalShade
* @param mask the initial mask state
* @param flags the flags to set
* @return the flag mask updated
*/
public static int parseFlags(int mask, final String...flags) {
for(String name : flags) {
boolean b = true;
if (name.charAt(0) == '+') {
name = name.substring(1);
} else if (name.charAt(0) == '-') {
name = name.substring(1);
b = false;
}
for(int flag = 0; flag < NAMES.length; ++flag) {
if (NAMES[flag].equals(name)) {
if (b) {
mask |= (1 << flag);
} else {
mask &= ~(1 << flag);
}
break;
}
}
}
return mask;
}
/**
* Sets this option flags using the +/- syntax.
* @param opts the option flags
*/
public void setFlags(final String[] opts) {
flags = parseFlags(flags, opts);
}
/**
* The MathContext instance used for +,-,/,*,% operations on big decimals.
* @return the math context
*/
public MathContext getMathContext() {
return mathContext;
}
/**
* The BigDecimal scale used for comparison and coercion operations.
* @return the scale
*/
public int getMathScale() {
return mathScale;
}
/**
* Checks whether evaluation will attempt resolving antish variable names.
* @return true if antish variables are solved, false otherwise
*/
public boolean isAntish() {
return isSet(ANTISH, flags);
}
/**
* Checks whether evaluation will throw JexlException.Cancel (true) or
* return null (false) if interrupted.
* @return true when cancellable, false otherwise
*/
public boolean isCancellable() {
return isSet(CANCELLABLE, flags);
}
/**
* Checks whether runtime variable scope is lexical.
* <p>If true, lexical scope applies to local variables and parameters.
* Redefining a variable in the same lexical unit will generate errors.
* @return true if scope is lexical, false otherwise
*/
public boolean isLexical() {
return isSet(LEXICAL, flags);
}
/**
* Checks whether local variables shade global ones.
* <p>After a symbol is defined as local, dereferencing it outside its
* scope will trigger an error instead of seeking a global variable of the
* same name. To further reduce potential naming ambiguity errors,
* global variables (ie non local) must be declared to be assigned (@link JexlContext#has(String) )
* when this flag is on; attempting to set an undeclared global variables will
* raise an error.
* @return true if lexical shading is applied, false otherwise
*/
public boolean isLexicalShade() {
return isSet(SHADE, flags);
}
/**
* Checks whether the engine considers null in navigation expression as
* errors during evaluation..
* @return true if safe, false otherwise
*/
public boolean isSafe() {
return isSet(SAFE, flags);
}
/**
* Checks whether the engine will throw a {@link JexlException} when an
* error is encountered during evaluation.
* @return true if silent, false otherwise
*/
public boolean isSilent() {
return isSet(SILENT, flags);
}
/**
* Checks whether the engine considers unknown variables, methods and
* constructors as errors during evaluation.
* @return true if strict, false otherwise
*/
public boolean isStrict() {
return isSet(STRICT, flags);
}
/**
* Checks whether the arithmetic triggers errors during evaluation when null
* is used as an operand.
* @return true if strict, false otherwise
*/
public boolean isStrictArithmetic() {
return strictArithmetic;
}
/**
* Sets whether the engine will attempt solving antish variable names from
* context.
* @param flag true if antish variables are solved, false otherwise
*/
public void setAntish(final boolean flag) {
flags = set(ANTISH, flags, flag);
}
/**
* Sets whether the engine will throw JexlException.Cancel (true) or return
* null (false) when interrupted during evaluation.
* @param flag true when cancellable, false otherwise
*/
public void setCancellable(final boolean flag) {
flags = set(CANCELLABLE, flags, flag);
}
/**
* Sets whether the engine uses a strict block lexical scope during
* evaluation.
* @param flag true if lexical scope is used, false otherwise
*/
public void setLexical(final boolean flag) {
flags = set(LEXICAL, flags, flag);
}
/**
* Sets whether the engine strictly shades global variables.
* Local symbols shade globals after definition and creating global
* variables is prohibited during evaluation.
* If setting to lexical shade, lexical scope is also set.
* @param flag true if creation is allowed, false otherwise
*/
public void setLexicalShade(final boolean flag) {
flags = set(SHADE, flags, flag);
if (flag) {
flags = set(LEXICAL, flags, true);
}
}
/**
* Sets the arithmetic math context.
* @param mcontext the context
*/
public void setMathContext(final MathContext mcontext) {
this.mathContext = mcontext;
}
/**
* Sets the arithmetic math scale.
* @param mscale the scale
*/
public void setMathScale(final int mscale) {
this.mathScale = mscale;
}
/**
* Sets whether the engine considers null in navigation expression as errors
* during evaluation.
* @param flag true if safe, false otherwise
*/
public void setSafe(final boolean flag) {
flags = set(SAFE, flags, flag);
}
/**
* Sets whether the engine will throw a {@link JexlException} when an error
* is encountered during evaluation.
* @param flag true if silent, false otherwise
*/
public void setSilent(final boolean flag) {
flags = set(SILENT, flags, flag);
}
/**
* Sets whether the engine considers unknown variables, methods and
* constructors as errors during evaluation.
* @param flag true if strict, false otherwise
*/
public void setStrict(final boolean flag) {
flags = set(STRICT, flags, flag);
}
/**
* Sets the strict arithmetic flag.
* @param stricta true or false
*/
public void setStrictArithmetic(final boolean stricta) {
this.strictArithmetic = stricta;
}
/**
* Whether these options are immutable at runtime.
* <p>Expert mode; allows instance handled through context to be shared
* instead of copied.
* @param flag true if shared, false if not
*/
public void setSharedInstance(final boolean flag) {
flags = set(SHARED, flags, flag);
}
/**
* @return false if a copy of these options is used during execution,
* true if those can potentially be modified
*/
public boolean isSharedInstance() {
return isSet(SHARED, flags);
}
/**
* Set options from engine.
* @param jexl the engine
* @return this instance
*/
public JexlOptions set(final JexlEngine jexl) {
if (jexl instanceof Engine) {
((Engine) jexl).optionsSet(this);
}
return this;
}
/**
* Set options from options.
* @param src the options
* @return this instance
*/
public JexlOptions set(final JexlOptions src) {
mathContext = src.mathContext;
mathScale = src.mathScale;
strictArithmetic = src.strictArithmetic;
flags = src.flags;
namespaces = src.namespaces;
return this;
}
/**
* Gets the optional map of namespaces.
* @return the map of namespaces, may be empty, not null
*/
public Map<String, Object> getNamespaces() {
return namespaces;
}
/**
* Sets the optional map of namespaces.
* @param ns a namespaces map
*/
public void setNamespaces(final Map<String, Object> ns) {
this.namespaces = ns == null? Collections.emptyMap() : ns;
}
/**
* Creates a copy of this instance.
* @return a copy
*/
public JexlOptions copy() {
return new JexlOptions().set(this);
}
}