添加jexl依赖,添加脚本执行器

main
youHong.ai 2023-03-04 00:48:34 +08:00
parent 8c84a01a69
commit 2bbac377aa
193 changed files with 43107 additions and 9 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,551 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.Engine;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox;
import aiyh.utils.tool.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 error messages will carry debugging information. */
private Boolean debug = null;
/** Whether interrupt throws JexlException.Cancel. */
private Boolean cancellable = null;
/** The options. */
private final JexlOptions options = new JexlOptions();
/** Whether getVariables considers all potential equivalent syntactic forms. */
private int collectMode = 1;
/** The {@link JexlArithmetic} instance. */
private JexlArithmetic arithmetic = null;
/** The cache size. */
private int cache = -1;
/** The stack overflow limit. */
private int stackOverflow = Integer.MAX_VALUE;
/** 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;
/** The features. */
private JexlFeatures features = null;
/**
* Sets the JexlUberspect instance the engine will use.
*
* @param u the uberspect
* @return this builder
*/
public JexlBuilder uberspect(final 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(final JexlUberspect.ResolverStrategy rs) {
this.strategy = rs;
return this;
}
/** @return the strategy strategy */
public JexlUberspect.ResolverStrategy strategy() {
return this.strategy;
}
/** @return the current set of options */
public JexlOptions options() {
return options;
}
/**
* Sets the JexlArithmetic instance the engine will use.
*
* @param a the arithmetic
* @return this builder
*/
public JexlBuilder arithmetic(final JexlArithmetic a) {
this.arithmetic = a;
options.setStrictArithmetic(a.isStrict());
options.setMathContext(a.getMathContext());
options.setMathScale(a.getMathScale());
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(final JexlSandbox box) {
this.sandbox = box;
return this;
}
/** @return the sandbox */
public JexlSandbox sandbox() {
return this.sandbox;
}
/**
* Sets the features the engine will use as a base by default.
* <p>Note that the script flag will be ignored; the engine will be able to parse expressions and scripts.
* <p>Note also that these will apply to template expressions and scripts.
* <p>As a last remark, if lexical or lexicalShade are set as features, this
* method will also set the corresponding options.
* @param f the features
* @return this builder
*/
public JexlBuilder features(final JexlFeatures f) {
this.features = f;
if (features != null) {
if (features.isLexical()) {
options.setLexical(true);
}
if (features.isLexicalShade()) {
options.setLexicalShade(true);
}
}
return this;
}
/** @return the features */
public JexlFeatures features() {
return this.features;
}
/**
* Sets the o.a.c.Log instance to use.
*
* @param log the logger
* @return this builder
*/
public JexlBuilder logger(final Log log) {
this.logger = log;
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(final 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
* @deprecated since 3.1 use {@link #charset(Charset)} instead
*/
@Deprecated
public JexlBuilder loader(final Charset arg) {
return charset(arg);
}
/**
* Sets the charset to use.
*
* @param arg the charset
* @return this builder
* @since 3.1
*/
public JexlBuilder charset(final Charset arg) {
this.charset = arg;
return this;
}
/** @return the charset */
public Charset charset() {
return charset;
}
/**
* Sets whether the engine will resolve antish variable names.
*
* @param flag true means antish resolution is enabled, false disables it
* @return this builder
*/
public JexlBuilder antish(final boolean flag) {
options.setAntish(flag);
return this;
}
/** @return whether antish resolution is enabled */
public boolean antish() {
return options.isAntish();
}
/**
* Sets whether the engine is in lexical mode.
*
* @param flag true means lexical function scope is in effect, false implies non-lexical scoping
* @return this builder
* @since 3.2
*/
public JexlBuilder lexical(final boolean flag) {
options.setLexical(flag);
return this;
}
/** @return whether lexical scope is enabled */
public boolean lexical() {
return options.isLexical();
}
/**
* Sets whether the engine is in lexical shading mode.
*
* @param flag true means lexical shading is in effect, false implies no lexical shading
* @return this builder
* @since 3.2
*/
public JexlBuilder lexicalShade(final boolean flag) {
options.setLexicalShade(flag);
return this;
}
/** @return whether lexical shading is enabled */
public boolean lexicalShade() {
return options.isLexicalShade();
}
/**
* 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(final boolean flag) {
options.setSilent(flag);
return this;
}
/** @return the silent error handling flag */
public Boolean silent() {
return options.isSilent();
}
/**
* 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(final boolean flag) {
options.setStrict(flag);
return this;
}
/** @return true if strict, false otherwise */
public Boolean strict() {
return options.isStrict();
}
/**
* Sets whether the engine considers dereferencing null in navigation expressions
* as errors or evaluates them as null.
* <p><code>x.y()</code> if x is null throws an exception when not safe,
* return null and warns if it is.<p>
*
* @param flag true means safe navigation, false throws exception when dereferencing null
* @return this builder
*/
public JexlBuilder safe(final boolean flag) {
options.setSafe(flag);
return this;
}
/** @return true if safe, false otherwise */
public Boolean safe() {
return options.isSafe();
}
/**
* 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(final 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
* @since 3.1
*/
public JexlBuilder cancellable(final boolean flag) {
this.cancellable = flag;
options.setCancellable(flag);
return this;
}
/**
* @return the cancellable information flag
* @since 3.1
*/
public Boolean cancellable() {
return this.cancellable;
}
/**
* Sets whether the engine variable collectors considers all potential forms of variable syntaxes.
*
* @param flag true means var collections considers constant array accesses equivalent to dotted references
* @return this builder
* @since 3.2
*/
public JexlBuilder collectAll(final boolean flag) {
return collectMode(flag? 1 : 0);
}
/**
* Experimental collector mode setter.
*
* @param mode 0 or 1 as equivalents to false and true, other values are experimental
* @return this builder
* @since 3.2
*/
public JexlBuilder collectMode(final int mode) {
this.collectMode = mode;
return this;
}
/**
* @return true if variable collection follows strict syntactic rule
* @since 3.2
*/
public boolean collectAll() {
return this.collectMode != 0;
}
/**
* @return 0 if variable collection follows strict syntactic rule
* @since 3.2
*/
public int collectMode() {
return this.collectMode;
}
/**
* 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 constructor 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(final Map<String, Object> ns) {
options.setNamespaces(ns);
return this;
}
/**
* @return the map of namespaces.
*/
public Map<String, Object> namespaces() {
return options.getNamespaces();
}
/**
* 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(final 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(final int length) {
this.cacheThreshold = length > 0? length : CACHE_THRESHOLD;
return this;
}
/**
* @return the cache threshold
*/
public int cacheThreshold() {
return cacheThreshold;
}
/**
* Sets the number of script/expression evaluations that can be stacked.
* @param size if not strictly positive, limit is reached when java StackOverflow is thrown.
* @return this builder
*/
public JexlBuilder stackOverflow(final int size) {
this.stackOverflow = size;
return this;
}
/**
* @return the cache size
*/
public int stackOverflow() {
return stackOverflow;
}
/**
* @return a {@link JexlEngine} instance
*/
public JexlEngine create() {
return new Engine(this);
}
}

View File

@ -0,0 +1,203 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Manages variables which can be referenced in a JEXL expression.
*
* <p>JEXL variable names in their simplest form are 'java-like' identifiers.
* JEXL also considers 'ant' inspired variables expressions as valid.
* For instance, the expression 'x.y.z' is an 'antish' variable and will be resolved as a whole by the context,
* i.e. using the key "x.y.z". This proves to be useful to solve "fully qualified class names".</p>
*
* <p>The interpreter variable resolution algorithm will try the different sequences of identifiers till it finds
* one that exists in the context; if "x" is an object known in the context (JexlContext.has("x") returns true),
* "x.y" will <em>not</em> be looked up in the context but will most likely refer to "x.getY()".</p>
*
* <p>Note that JEXL may use '$jexl' and '$ujexl' variables for internal purpose; setting or getting those
* variables may lead to unexpected results unless specified otherwise.</p>
*
* @since 1.0
*/
public interface JexlContext {
/**
* Gets the value of a variable.
*
* @param name the variable's name
* @return the value
*/
Object get(String name);
/**
* Sets the value of a variable.
*
* @param name the variable's name
* @param value the variable's value
*/
void set(String name, Object value);
/**
* Checks whether a variable is defined in this context.
*
* <p>A variable may be defined with a null value; this method checks whether the
* value is null or if the variable is undefined.</p>
*
* @param name the variable's name
* @return true if it exists, false otherwise
*/
boolean has(String name);
/**
* A marker interface of the JexlContext that declares how to resolve a namespace from its name;
* it is used by the interpreter during evaluation.
*
* <p>In JEXL, a namespace is an object that serves the purpose of encapsulating functions; for instance,
* the "math" namespace would be the proper object to expose functions like "log(...)", "sinus(...)", etc.</p>
* <p>
* In expressions like "ns:function(...)", the resolver is called with resolveNamespace("ns").
*
* <p>JEXL itself reserves 'jexl' and 'ujexl' as namespaces for internal purpose; resolving those may lead to
* unexpected results.</p>
*
* @since 3.0
*/
interface NamespaceResolver {
/**
* Resolves a namespace by its name.
*
* @param name the name
* @return the namespace object
*/
Object resolveNamespace(String name);
}
/**
* A marker interface of the JexlContext, NamespaceFunctor allows creating an instance
* to delegate namespace methods calls to.
*
* <p>The functor is created once during the lifetime of a script evaluation.</p>
*/
interface NamespaceFunctor {
/**
* Creates the functor object that will be used instead of the namespace.
*
* @param context the context
* @return the namespace functor instance
*/
Object createFunctor(JexlContext context);
}
/**
* A marker interface of the JexlContext that indicates the interpreter to put this context
* in the JexlEngine thread local context instance during evaluation.
* This allows user functions or methods to access the context during a call.
* Note that the usual caveats wrt using thread local apply (caching/leaking references, etc.); in particular,
* keeping a reference to such a context is to be considered with great care and caution.
* It should also be noted that sharing such a context between threads should implicate synchronizing variable
* accessing the implementation class.
*
* @see JexlEngine#setThreadContext(ThreadLocal)
* @see JexlEngine#getThreadContext()
*/
interface ThreadLocal extends JexlContext {
// no specific method
}
/**
* A marker interface of the JexlContext that processes annotations.
* It is used by the interpreter during evaluation to execute annotation evaluations.
* <p>If the JexlContext is not an instance of an AnnotationProcessor, encountering an annotation will generate
* an error or a warning depending on the engine strictness.
*
* @since 3.1
*/
interface AnnotationProcessor {
/**
* Processes an annotation.
* <p>All annotations are processed through this method; the statement 'call' is to be performed within
* the processAnnotation method. The implementation <em>must</em> perform the call explicitly.
* <p>The arguments and the statement <em>must not</em> be referenced or cached for longer than the duration
* of the processAnnotation call.
*
* @param name the annotation name
* @param args the arguments of the annotation, evaluated as arguments of this call
* @param statement the statement that was annotated; the processor should invoke this statement 'call' method
* @return the result of statement.call()
* @throws Exception if annotation processing fails
*/
Object processAnnotation(String name, Object[] args, Callable<Object> statement) throws Exception;
}
/**
* A marker interface of the JexlContext that exposes runtime evaluation options.
*
* @since 3.2
*/
interface OptionsHandle {
/**
* Retrieves the current set of options though the context.
* <p>
* This method will be called once at beginning of evaluation and an interpreter private copy
* of the context handled JexlOptions instance used for the duration of the execution;
* the context handled JexlOptions instance being only used as the source of that copy,
* it can safely alter its boolean flags during execution with no effect, avoiding any behavior ambiguity.
*
* @return the engine options
*/
JexlOptions getEngineOptions();
}
/**
* A marker interface of the JexlContext that processes pragmas.
* It is called by the engine before interpreter creation; as a marker of
* JexlContext, it is expected to have access and interact with the context
* instance.
*
* @since 3.2
*/
interface PragmaProcessor {
/**
* Process one pragma.
*
* @param key the key
* @param value the value
*/
void processPragma(String key, Object value);
}
/**
* A marker interface of the JexlContext sharing a cancelling flag.
* <p>A script running in a thread can thus be notified through this reference
* of its cancellation through the context. It uses the same interpreter logic
* that reacts to cancellation and is an alternative to using callable() and/or
* interrupting script interpreter threads.
*
* @since 3.2
*/
interface CancellationHandle {
/**
* @return a cancelable boolean used by the interpreter
*/
AtomicBoolean getCancellation();
}
}

View File

@ -0,0 +1,641 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import java.io.*;
import java.math.MathContext;
import java.net.URL;
import java.nio.charset.Charset;
/**
* Creates and evaluates JexlExpression and JexlScript objects.
* Determines the behavior of expressions and scripts during their evaluation with respect to:
* <ul>
* <li>Introspection, see {@link JexlUberspect}</li>
* <li>Arithmetic and comparison, see {@link JexlArithmetic}</li>
* <li>Error reporting</li>
* <li>Logging</li>
* </ul>
*
* <p>Note that methods that evaluate expressions may throw <em>unchecked</em> exceptions;
* The {@link JexlException} are thrown in "non-silent" mode but since these are
* RuntimeException, user-code <em>should</em> catch them wherever most appropriate.</p>
*
* @since 2.0
*/
public abstract class JexlEngine {
/** A marker singleton for invocation failures in tryInvoke. */
public static final Object TRY_FAILED = new FailObject();
/** The failure marker class. */
private static final class FailObject {
/**
* Default ctor.
*/
private FailObject() {
}
@Override
public String toString() {
return "tryExecute failed";
}
}
/**
* The thread local context.
*/
protected static final ThreadLocal<JexlContext.ThreadLocal> CONTEXT =
new ThreadLocal<>();
/**
* Accesses the current thread local context.
*
* @return the context or null
*/
public static JexlContext.ThreadLocal getThreadContext() {
return CONTEXT.get();
}
/**
* The thread local engine.
*/
protected static final ThreadLocal<JexlEngine> ENGINE =
new ThreadLocal<>();
/**
* Accesses the current thread local engine.
* <p>Advanced: you should only use this to retrieve the engine within a method/ctor called through the evaluation
* of a script/expression.</p>
*
* @return the engine or null
*/
public static JexlEngine getThreadEngine() {
return ENGINE.get();
}
/**
* Sets the current thread local context.
* <p>This should only be used carefully, for instance when re-evaluating a "stored" script that requires a
* given Namespace resolver. Remember to synchronize access if context is shared between threads.
*
* @param tls the thread local context to set
*/
public static void setThreadContext(final JexlContext.ThreadLocal tls) {
CONTEXT.set(tls);
}
/**
* Script evaluation options.
* <p>The JexlContext used for evaluation can implement this interface to alter behavior.</p>
*
* @deprecated 3.2
*/
@Deprecated
public interface Options {
/**
* The charset used for parsing.
*
* @return the charset
*/
Charset getCharset();
/**
* Sets whether the engine will throw a {@link JexlException} when an error is encountered during evaluation.
*
* @return true if silent, false otherwise
*/
Boolean isSilent();
/**
* Checks whether the engine considers unknown variables, methods, functions and constructors as errors or
* evaluates them as null.
*
* @return true if strict, false otherwise
*/
Boolean isStrict();
/**
* Checks whether the arithmetic triggers errors during evaluation when null is used as an operand.
*
* @return true if strict, false otherwise
*/
Boolean isStrictArithmetic();
/**
* Whether evaluation will throw JexlException.Cancel (true) or return null (false) when interrupted.
*
* @return true when cancellable, false otherwise
* @since 3.1
*/
Boolean isCancellable();
/**
* The MathContext instance used for +,-,/,*,% operations on big decimals.
*
* @return the math context
*/
MathContext getArithmeticMathContext();
/**
* The BigDecimal scale used for comparison and coercion operations.
*
* @return the scale
*/
int getArithmeticMathScale();
}
/** Default features. */
public static final JexlFeatures DEFAULT_FEATURES = new JexlFeatures();
/**
* An empty/static/non-mutable JexlContext singleton used instead of null context.
*/
public static final JexlContext EMPTY_CONTEXT = new EmptyContext();
/**
* The empty context class, public for instrospection.
*/
public static final class EmptyContext implements JexlContext {
/**
* Default ctor.
*/
private EmptyContext() {
}
@Override
public Object get(final String name) {
return null;
}
@Override
public boolean has(final String name) {
return false;
}
@Override
public void set(final String name, final Object value) {
throw new UnsupportedOperationException("Not supported in void context.");
}
}
/**
* An empty/static/non-mutable JexlNamespace singleton used instead of null namespace.
*/
public static final JexlContext.NamespaceResolver EMPTY_NS = new EmptyNamespaceResolver();
/**
* The empty/static/non-mutable JexlNamespace class, public for instrospection.
*/
public static final class EmptyNamespaceResolver implements JexlContext.NamespaceResolver {
/**
* Default ctor.
*/
private EmptyNamespaceResolver() {
}
@Override
public Object resolveNamespace(final String name) {
return null;
}
}
/** The default Jxlt cache size. */
private static final int JXLT_CACHE_SIZE = 256;
/**
* Gets the charset used for parsing.
*
* @return the charset
*/
public abstract Charset getCharset();
/**
* Gets this engine underlying {@link JexlUberspect}.
*
* @return the uberspect
*/
public abstract JexlUberspect getUberspect();
/**
* Gets this engine underlying {@link JexlArithmetic}.
*
* @return the arithmetic
*/
public abstract JexlArithmetic getArithmetic();
/**
* Checks whether this engine is in debug mode.
*
* @return true if debug is on, false otherwise
*/
public abstract boolean isDebug();
/**
* Checks whether this engine throws JexlException during evaluation.
*
* @return true if silent, false (default) otherwise
*/
public abstract boolean isSilent();
/**
* Checks whether this engine considers unknown variables, methods, functions and constructors as errors.
*
* @return true if strict, false otherwise
*/
public abstract boolean isStrict();
/**
* Checks whether this engine will throw JexlException.Cancel (true) or return null (false) when interrupted
* during an execution.
*
* @return true if cancellable, false otherwise
*/
public abstract boolean isCancellable();
/**
* Sets the class loader used to discover classes in 'new' expressions.
* <p>This method is <em>not</em> thread safe; it may be called after JexlEngine
* initialization and allow scripts to use new classes definitions.</p>
*
* @param loader the class loader to use
*/
public abstract void setClassLoader(ClassLoader loader);
/**
* Creates a new {@link JxltEngine} instance using this engine.
*
* @return a JEXL Template engine
*/
public JxltEngine createJxltEngine() {
return createJxltEngine(true);
}
/**
* Creates a new {@link JxltEngine} instance using this engine.
*
* @param noScript whether the JxltEngine only allows Jexl expressions or scripts
* @return a JEXL Template engine
*/
public JxltEngine createJxltEngine(final boolean noScript) {
return createJxltEngine(noScript, JXLT_CACHE_SIZE, '$', '#');
}
/**
* Creates a new instance of {@link JxltEngine} using this engine.
*
* @param noScript whether the JxltEngine only allows JEXL expressions or scripts
* @param cacheSize the number of expressions in this cache, default is 256
* @param immediate the immediate template expression character, default is '$'
* @param deferred the deferred template expression character, default is '#'
* @return a JEXL Template engine
*/
public abstract JxltEngine createJxltEngine(boolean noScript, int cacheSize, char immediate, char deferred);
/**
* Clears the expression cache.
*/
public abstract void clearCache();
/**
* Creates an JexlExpression from a String containing valid JEXL syntax.
* This method parses the expression which must contain either a reference or an expression.
*
* @param info An info structure to carry debugging information if needed
* @param expression A String containing valid JEXL syntax
* @return An {@link JexlExpression} which can be evaluated using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
public abstract JexlExpression createExpression(JexlInfo info, String expression);
/**
* Creates a JexlExpression from a String containing valid JEXL syntax.
* This method parses the expression which must contain either a reference or an expression.
*
* @param expression A String containing valid JEXL syntax
* @return An {@link JexlExpression} which can be evaluated using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
public final JexlExpression createExpression(final String expression) {
return createExpression(null, expression);
}
/**
* Creates a JexlScript from a String containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param features A set of features that will be enforced during parsing
* @param info An info structure to carry debugging information if needed
* @param source A string containing valid JEXL syntax
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
public abstract JexlScript createScript(JexlFeatures features, JexlInfo info, String source, String... names);
/**
* Creates a JexlScript from a String containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param info An info structure to carry debugging information if needed
* @param source A string containing valid JEXL syntax
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
public final JexlScript createScript(final JexlInfo info, final String source, final String... names) {
return createScript(null, info, source, names);
}
/**
* Creates a Script from a String containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param scriptText A String containing valid JEXL syntax
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script.
*/
public final JexlScript createScript(final String scriptText) {
return createScript(null, null, scriptText, (String[]) null);
}
/**
* Creates a Script from a String containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param source A String containing valid JEXL syntax
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
public final JexlScript createScript(final String source, final String... names) {
return createScript(null, null, source, names);
}
/**
* Creates a Script from a {@link File} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final File scriptFile) {
return createScript(null, null, readSource(scriptFile), (String[]) null);
}
/**
* Creates a Script from a {@link File} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file.
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final File scriptFile, final String... names) {
return createScript(null, null, readSource(scriptFile), names);
}
/**
* Creates a Script from a {@link File} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param info An info structure to carry debugging information if needed
* @param scriptFile A {@link File} containing valid JEXL syntax. Must not be null. Must be a readable file.
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final JexlInfo info, final File scriptFile, final String... names) {
return createScript(null, info, readSource(scriptFile), names);
}
/**
* Creates a Script from a {@link URL} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final URL scriptUrl) {
return createScript(null, readSource(scriptUrl), (String[]) null);
}
/**
* Creates a Script from a {@link URL} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null.
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final URL scriptUrl, final String... names) {
return createScript(null, null, readSource(scriptUrl), names);
}
/**
* Creates a Script from a {@link URL} containing valid JEXL syntax.
* This method parses the script and validates the syntax.
*
* @param info An info structure to carry debugging information if needed
* @param scriptUrl A {@link URL} containing valid JEXL syntax. Must not be null.
* @param names The script parameter names used during parsing; a corresponding array of arguments containing
* values should be used during evaluation.
* @return A {@link JexlScript} which can be executed with a {@link JexlContext}.
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(final JexlInfo info, final URL scriptUrl, final String... names) {
return createScript(null, info, readSource(scriptUrl), names);
}
/**
* Accesses properties of a bean using an expression.
* <p>
* jexl.get(myobject, "foo.bar"); should equate to
* myobject.getFoo().getBar(); (or myobject.getFoo().get("bar"))
* </p>
* <p>
* If the JEXL engine is silent, errors will be logged through its logger as warning.
* </p>
*
* @param bean the bean to get properties from
* @param expr the property expression
* @return the value of the property
* @throws JexlException if there is an error parsing the expression or during evaluation
*/
public abstract Object getProperty(Object bean, String expr);
/**
* Accesses properties of a bean using an expression.
* <p>
* If the JEXL engine is silent, errors will be logged through its logger as warning.
* </p>
*
* @param context the evaluation context
* @param bean the bean to get properties from
* @param expr the property expression
* @return the value of the property
* @throws JexlException if there is an error parsing the expression or during evaluation
*/
public abstract Object getProperty(JexlContext context, Object bean, String expr);
/**
* Assign properties of a bean using an expression.
* <p>
* jexl.set(myobject, "foo.bar", 10); should equate to
* myobject.getFoo().setBar(10); (or myobject.getFoo().put("bar", 10) )
* </p>
* <p>
* If the JEXL engine is silent, errors will be logged through its logger as warning.
* </p>
*
* @param bean the bean to set properties in
* @param expr the property expression
* @param value the value of the property
* @throws JexlException if there is an error parsing the expression or during evaluation
*/
public abstract void setProperty(Object bean, String expr, Object value);
/**
* Assign properties of a bean using an expression. <p> If the JEXL engine is silent, errors will be logged through
* its logger as warning. </p>
*
* @param context the evaluation context
* @param bean the bean to set properties in
* @param expr the property expression
* @param value the value of the property
* @throws JexlException if there is an error parsing the expression or during evaluation
*/
public abstract void setProperty(JexlContext context, Object bean, String expr, Object value);
/**
* Invokes an object's method by name and arguments.
*
* @param obj the method's invoker object
* @param meth the method's name
* @param args the method's arguments
* @return the method returned value or null if it failed and engine is silent
* @throws JexlException if method could not be found or failed and engine is not silent
*/
public abstract Object invokeMethod(Object obj, String meth, Object... args);
/**
* Creates a new instance of an object using the most appropriate constructor based on the arguments.
*
* @param <T> the type of object
* @param clazz the class to instantiate
* @param args the constructor arguments
* @return the created object instance or null on failure when silent
*/
public abstract <T> T newInstance(Class<? extends T> clazz, Object... args);
/**
* Creates a new instance of an object using the most appropriate constructor based on the arguments.
*
* @param clazz the name of the class to instantiate resolved through this engine's class loader
* @param args the constructor arguments
* @return the created object instance or null on failure when silent
*/
public abstract Object newInstance(String clazz, Object... args);
/**
* Creates a JexlInfo instance.
*
* @param fn url/file/template/script user given name
* @param l line number
* @param c column number
* @return a JexlInfo instance
*/
public JexlInfo createInfo(final String fn, final int l, final int c) {
return new JexlInfo(fn, l, c);
}
/**
* Create an information structure for dynamic set/get/invoke/new.
* <p>This gathers the class, method and line number of the first calling method
* outside of o.a.c.jexl3.</p>
*
* @return a JexlInfo instance
*/
public JexlInfo createInfo() {
return new JexlInfo();
}
/**
* Creates a string from a reader.
*
* @param reader to be read.
* @return the contents of the reader as a String.
* @throws IOException on any error reading the reader.
*/
protected static String toString(final BufferedReader reader) throws IOException {
final StringBuilder buffer = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line).append('\n');
}
return buffer.toString();
}
/**
* Reads a JEXL source from a File.
*
* @param file the script file
* @return the source
*/
protected String readSource(final File file) {
if (file == null) {
throw new NullPointerException("source file is null");
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),
getCharset()))) {
return toString(reader);
} catch (final IOException xio) {
throw new JexlException(createInfo(file.toString(), 1, 1), "could not read source File", xio);
}
}
/**
* Reads a JEXL source from an URL.
*
* @param url the script url
* @return the source
*/
protected String readSource(final URL url) {
if (url == null) {
throw new NullPointerException("source URL is null");
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), getCharset()))) {
return toString(reader);
} catch (final IOException xio) {
throw new JexlException(createInfo(url.toString(), 1, 1), "could not read source URL", xio);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.util.concurrent.Callable;
/**
* Represents a single JEXL expression.
* <p>
* This simple interface provides access to the underlying textual expression through
* {@link JexlExpression#getSourceText()}.
* </p>
*
* <p>
* An expression is different than a script - it is simply a reference to
* a single expression, not to multiple statements.
* This implies 'if','for','while','var' and blocks '{'... '}'are <em>not</em> allowed in expressions.
* </p>
* <p>Do <em>not</em> create classes that implement this interface; delegate or compose instead.</p>
*
* @since 1.0
*/
public interface JexlExpression {
/**
* Evaluates the expression with the variables contained in the
* supplied {@link JexlContext}.
*
* @param context A JexlContext containing variables.
* @return The result of this evaluation
* @throws JexlException on any error
*/
Object evaluate(JexlContext context);
/**
* Returns the source text of this expression.
*
* @return the source text
*/
String getSourceText();
/**
* Recreates the source text of this expression from the internal syntactic tree.
*
* @return the source text
*/
String getParsedText();
/**
* Creates a Callable from this expression.
*
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
*
* @param context the context
* @return the callable
* @since 3.1
*/
Callable<Object> callable(JexlContext context);
}

View File

@ -0,0 +1,540 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import java.util.Objects;
import java.util.function.Predicate;
/**
* A set of language feature options.
* These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
* subclass of JexlException.Parsing) when disabled.
* <ul>
* <li>Registers: register syntax (#number), used internally for {g,s}etProperty
* <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
* <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
* <li>Lexical: lexical scope, prevents redefining local variables
* <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
* <li>Side Effect : assigning/modifying values on any variables or left-value
* <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
* <li>New Instance: creating an instance using new(...)
* <li>Loops: loop constructs (while(true), for(...))
* <li>Lambda: function definitions (()-&gt;{...}, function(...) ).
* <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
* - including namespace prefixes - available
* <li>Structured literals: arrays, lists, maps, sets, ranges
* <li>Pragmas: #pragma x y
* <li>Annotation: @annotation statement;
* </ul>
* @since 3.2
*/
public final class JexlFeatures {
/** The feature flags. */
private long flags;
/** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */
private Set<String> reservedNames;
/** The namespace names. */
private Predicate<String> nameSpaces;
/** The false predicate. */
public static final Predicate<String> TEST_STR_FALSE = (s)->false;
/** Te feature names (for toString()). */
private static final String[] F_NAMES = {
"register", "reserved variable", "local variable", "assign/modify",
"global assign/modify", "array reference", "create instance", "loop", "function",
"method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade"
};
/** Registers feature ordinal. */
private static final int REGISTER = 0;
/** Reserved name feature ordinal. */
public static final int RESERVED = 1;
/** Locals feature ordinal. */
public static final int LOCAL_VAR = 2;
/** Side-effects feature ordinal. */
public static final int SIDE_EFFECT = 3;
/** Global side-effects feature ordinal. */
public static final int SIDE_EFFECT_GLOBAL = 4;
/** Array get is allowed on expr. */
public static final int ARRAY_REF_EXPR = 5;
/** New-instance feature ordinal. */
public static final int NEW_INSTANCE = 6;
/** Loops feature ordinal. */
public static final int LOOP = 7;
/** Lambda feature ordinal. */
public static final int LAMBDA = 8;
/** Lambda feature ordinal. */
public static final int METHOD_CALL = 9;
/** Structured literal feature ordinal. */
public static final int STRUCTURED_LITERAL = 10;
/** Pragma feature ordinal. */
public static final int PRAGMA = 11;
/** Annotation feature ordinal. */
public static final int ANNOTATION = 12;
/** Script feature ordinal. */
public static final int SCRIPT = 13;
/** Lexical feature ordinal. */
public static final int LEXICAL = 14;
/** Lexical shade feature ordinal. */
public static final int LEXICAL_SHADE = 15;
/**
* Creates an all-features-enabled instance.
*/
public JexlFeatures() {
flags = (1L << LOCAL_VAR)
| (1L << SIDE_EFFECT)
| (1L << SIDE_EFFECT_GLOBAL)
| (1L << ARRAY_REF_EXPR)
| (1L << NEW_INSTANCE)
| (1L << LOOP)
| (1L << LAMBDA)
| (1L << METHOD_CALL)
| (1L << STRUCTURED_LITERAL)
| (1L << PRAGMA)
| (1L << ANNOTATION)
| (1L << SCRIPT);
reservedNames = Collections.emptySet();
nameSpaces = TEST_STR_FALSE;
}
/**
* Copy constructor.
* @param features the feature to copy from
*/
public JexlFeatures(final JexlFeatures features) {
this.flags = features.flags;
this.reservedNames = features.reservedNames;
this.nameSpaces = features.nameSpaces;
}
@Override
public int hashCode() { //CSOFF: MagicNumber
int hash = 3;
hash = 53 * hash + (int) (this.flags ^ (this.flags >>> 32));
hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final JexlFeatures other = (JexlFeatures) obj;
if (this.flags != other.flags) {
return false;
}
if (!Objects.equals(this.reservedNames, other.reservedNames)) {
return false;
}
return true;
}
/**
* The text corresponding to a feature code.
* @param feature the feature number
* @return the feature name
*/
public static String stringify(final int feature) {
return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
}
/**
* Sets a collection of reserved names precluding those to be used as local variables or parameter names.
* @param names the names to reserve
* @return this features instance
*/
public JexlFeatures reservedNames(final Collection<String> names) {
if (names == null || names.isEmpty()) {
reservedNames = Collections.emptySet();
} else {
reservedNames = Collections.unmodifiableSet(new TreeSet<String>(names));
}
setFeature(RESERVED, !reservedNames.isEmpty());
return this;
}
/**
* @return the (unmodifiable) set of reserved names.
*/
public Set<String> getReservedNames() {
return reservedNames;
}
/**
* Checks whether a name is reserved.
* @param name the name to check
* @return true if reserved, false otherwise
*/
public boolean isReservedName(final String name) {
return name != null && reservedNames.contains(name);
}
/**
* Sets a test to determine namespace declaration.
* @param names the name predicate
* @return this features instance
*/
public JexlFeatures namespaceTest(final Predicate<String> names) {
nameSpaces = names == null? TEST_STR_FALSE : names;
return this;
}
/**
* @return the declared namespaces test.
*/
public Predicate<String> namespaceTest() {
return nameSpaces;
}
/**
* Sets a feature flag.
* @param feature the feature ordinal
* @param flag turn-on, turn off
*/
private void setFeature(final int feature, final boolean flag) {
if (flag) {
flags |= (1 << feature);
} else {
flags &= ~(1L << feature);
}
}
/**
* Gets a feature flag value.
* @param feature feature ordinal
* @return true if on, false if off
*/
private boolean getFeature(final int feature) {
return (flags & (1L << feature)) != 0L;
}
/**
* Sets whether register are enabled.
* <p>
* This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
* <p>
* When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures register(final boolean flag) {
setFeature(REGISTER, flag);
return this;
}
/**
* @return true if register syntax is enabled
*/
public boolean supportsRegister() {
return getFeature(REGISTER);
}
/**
* Sets whether local variables are enabled.
* <p>
* When disabled, parsing a script/expression using a local variable or parameter syntax
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures localVar(final boolean flag) {
setFeature(LOCAL_VAR, flag);
return this;
}
/**
* @return true if local variables syntax is enabled
*/
public boolean supportsLocalVar() {
return getFeature(LOCAL_VAR);
}
/**
* Sets whether side effect expressions on global variables (aka non local) are enabled.
* <p>
* When disabled, parsing a script/expression using syntactical constructs modifying variables
* <em>including all potentially ant-ish variables</em> will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures sideEffectGlobal(final boolean flag) {
setFeature(SIDE_EFFECT_GLOBAL, flag);
return this;
}
/**
* @return true if global variables can be assigned
*/
public boolean supportsSideEffectGlobal() {
return getFeature(SIDE_EFFECT_GLOBAL);
}
/**
* Sets whether side effect expressions are enabled.
* <p>
* When disabled, parsing a script/expression using syntactical constructs modifying variables
* or members will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures sideEffect(final boolean flag) {
setFeature(SIDE_EFFECT, flag);
return this;
}
/**
* @return true if side effects are enabled, false otherwise
*/
public boolean supportsSideEffect() {
return getFeature(SIDE_EFFECT);
}
/**
* Sets whether array references expressions are enabled.
* <p>
* When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
* will throw a parsing exception;
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures arrayReferenceExpr(final boolean flag) {
setFeature(ARRAY_REF_EXPR, flag);
return this;
}
/**
* @return true if array references can contain method call expressions, false otherwise
*/
public boolean supportsArrayReferenceExpr() {
return getFeature(ARRAY_REF_EXPR);
}
/**
* Sets whether method calls expressions are enabled.
* <p>
* When disabled, parsing a script/expression using 'obj.method()'
* will throw a parsing exception;
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures methodCall(final boolean flag) {
setFeature(METHOD_CALL, flag);
return this;
}
/**
* @return true if array references can contain expressions, false otherwise
*/
public boolean supportsMethodCall() {
return getFeature(METHOD_CALL);
}
/**
* Sets whether array/map/set literal expressions are enabled.
* <p>
* When disabled, parsing a script/expression creating one of these literals
* will throw a parsing exception;
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures structuredLiteral(final boolean flag) {
setFeature(STRUCTURED_LITERAL, flag);
return this;
}
/**
* @return true if array/map/set literal expressions are supported, false otherwise
*/
public boolean supportsStructuredLiteral() {
return getFeature(STRUCTURED_LITERAL);
}
/**
* Sets whether creating new instances is enabled.
* <p>
* When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
* using a class as functor will fail at runtime.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures newInstance(final boolean flag) {
setFeature(NEW_INSTANCE, flag);
return this;
}
/**
* @return true if creating new instances is enabled, false otherwise
*/
public boolean supportsNewInstance() {
return getFeature(NEW_INSTANCE);
}
/**
* Sets whether looping constructs are enabled.
* <p>
* When disabled, parsing a script/expression using syntactic looping constructs (for,while)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures loops(final boolean flag) {
setFeature(LOOP, flag);
return this;
}
/**
* @return true if loops are enabled, false otherwise
*/
public boolean supportsLoops() {
return getFeature(LOOP);
}
/**
* Sets whether lambda/function constructs are enabled.
* <p>
* When disabled, parsing a script/expression using syntactic lambda constructs (-&gt;,function)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures lambda(final boolean flag) {
setFeature(LAMBDA, flag);
return this;
}
/**
* @return true if lambda are enabled, false otherwise
*/
public boolean supportsLambda() {
return getFeature(LAMBDA);
}
/**
* Sets whether pragma constructs are enabled.
* <p>
* When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures pragma(final boolean flag) {
setFeature(PRAGMA, flag);
return this;
}
/**
* @return true if pragma are enabled, false otherwise
*/
public boolean supportsPragma() {
return getFeature(PRAGMA);
}
/**
* Sets whether annotation constructs are enabled.
* <p>
* When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures annotation(final boolean flag) {
setFeature(ANNOTATION, flag);
return this;
}
/**
* @return true if annotation are enabled, false otherwise
*/
public boolean supportsAnnotation() {
return getFeature(ANNOTATION);
}
/**
* Sets whether scripts constructs are enabled.
* <p>
* When disabled, parsing a script using syntactic script constructs (statements, ...)
* will throw a parsing exception.
* @param flag true to enable, false to disable
* @return this features instance
*/
public JexlFeatures script(final boolean flag) {
setFeature(SCRIPT, flag);
return this;
}
/**
* @return true if scripts are enabled, false otherwise
*/
public boolean supportsScript() {
return getFeature(SCRIPT);
}
/**
*
* @return true if expressions (aka not scripts) are enabled, false otherwise
*/
public boolean supportsExpression() {
return !getFeature(SCRIPT);
}
/**
* Sets whether syntactic lexical mode is enabled.
*
* @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping
* @return this features instance
*/
public JexlFeatures lexical(final boolean flag) {
setFeature(LEXICAL, flag);
return this;
}
/** @return whether lexical scope feature is enabled */
public boolean isLexical() {
return getFeature(LEXICAL);
}
/**
* Sets whether syntactic lexical shade is enabled.
*
* @param flag true means syntactic lexical shade is in effect and implies lexical scope
* @return this features instance
*/
public JexlFeatures lexicalShade(final boolean flag) {
setFeature(LEXICAL_SHADE, flag);
if (flag) {
setFeature(LEXICAL, true);
}
return this;
}
/** @return whether lexical shade feature is enabled */
public boolean isLexicalShade() {
return getFeature(LEXICAL_SHADE);
}
}

View File

@ -0,0 +1,201 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.Script;
/**
* Helper class to carry information such as a url/file name, line and column for
* debugging information reporting.
*/
public class JexlInfo {
/** line number. */
private final int line;
/** column number. */
private final int column;
/** name. */
private final String name;
/**
* @return the detailed information in case of an error
*/
public Detail getDetail() {
return null;
}
/**
* Describes errors more precisely.
*/
public interface Detail {
/**
* @return the start column on the line that triggered the error
*/
int start();
/**
* @return the end column on the line that triggered the error
*/
int end();
/**
* @return the actual part of code that triggered the error
*/
@Override
String toString();
}
/**
* Create info.
*
* @param source source name
* @param l line number
* @param c column number
*/
public JexlInfo(final String source, final int l, final int c) {
name = source;
line = l;
column = c;
}
/**
* Create an information structure for dynamic set/get/invoke/new.
* <p>This gathers the class, method and line number of the first calling method
* outside of o.a.c.jexl3.</p>
*/
public JexlInfo() {
final StackTraceElement[] stack = new Throwable().getStackTrace();
String cname = getClass().getName();
final String pkgname = getClass().getPackage().getName();
StackTraceElement se = null;
for (int s = 1; s < stack.length; ++s) {
se = stack[s];
final String className = se.getClassName();
if (!className.equals(cname)) {
// go deeper if called from jexl implementation classes
if (!className.startsWith(pkgname + ".internal.") && !className.startsWith(pkgname + ".Jexl")
&& !className.startsWith(pkgname + ".parser")) {
break;
}
cname = className;
}
}
this.name = se != null ? se.getClassName() + "." + se.getMethodName() + ":" + se.getLineNumber() : "?";
this.line = 0;
this.column = 0;
}
/**
* Creates info reusing the name.
*
* @param l the line
* @param c the column
* @return a new info instance
*/
public JexlInfo at(final int l, final int c) {
return new JexlInfo(name, l, c);
}
/**
* The copy constructor.
*
* @param copy the instance to copy
*/
protected JexlInfo(final JexlInfo copy) {
name = copy.getName();
line = copy.getLine();
column = copy.getColumn();
}
/**
* Formats this info in the form 'name&#064;line:column'.
*
* @return the formatted info
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(name != null ? name : "");
if (line > 0) {
sb.append("@");
sb.append(line);
if (column > 0) {
sb.append(":");
sb.append(column);
}
}
final Detail dbg = getDetail();
if (dbg != null) {
sb.append("![");
sb.append(dbg.start());
sb.append(",");
sb.append(dbg.end());
sb.append("]: '");
sb.append(dbg);
sb.append("'");
}
return sb.toString();
}
/**
* Gets the file/script/url name.
*
* @return template name
*/
public final String getName() {
return name;
}
/**
* Gets the line number.
*
* @return line number.
*/
public final int getLine() {
return line;
}
/**
* Gets the column number.
*
* @return the column.
*/
public final int getColumn() {
return column;
}
/**
* @return this instance or a copy without any decorations
*/
public JexlInfo detach() {
return this;
}
/**
* Gets the info from a script.
*
* @param script the script
* @return the info
*/
public static JexlInfo from(final JexlScript script) {
return script instanceof Script ? ((Script) script).getInfo() : null;
}
}

View File

@ -0,0 +1,403 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
/**
* The JEXL operators.
*
* These are the operators that are executed by JexlArithmetic methods.
*
* <p>Each of them associates a symbol to a method signature.
* For instance, '+' is associated to 'T add(L x, R y)'.</p>
*
* <p>The default JexlArithmetic implements generic versions of these methods using Object as arguments.
* You can use your own derived JexlArithmetic that override and/or overload those operator methods.
* Note that these are overloads by convention, not actual Java overloads.
* The following rules apply to operator methods:</p>
* <ul>
* <li>Operator methods should be public</li>
* <li>Operators return type should be respected when primitive (int, boolean,...)</li>
* <li>Operators may be overloaded multiple times with different signatures</li>
* <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation</li>
* </ul>
*
* @since 3.0
*/
public enum JexlOperator {
/**
* Add operator.
* <br><strong>Syntax:</strong> <code>x + y</code>
* <br><strong>Method:</strong> <code>T add(L x, R y);</code>.
* @see JexlArithmetic#add
*/
ADD("+", "add", 2),
/**
* Subtract operator.
* <br><strong>Syntax:</strong> <code>x - y</code>
* <br><strong>Method:</strong> <code>T subtract(L x, R y);</code>.
* @see JexlArithmetic#subtract
*/
SUBTRACT("-", "subtract", 2),
/**
* Multiply operator.
* <br><strong>Syntax:</strong> <code>x * y</code>
* <br><strong>Method:</strong> <code>T multiply(L x, R y);</code>.
* @see JexlArithmetic#multiply
*/
MULTIPLY("*", "multiply", 2),
/**
* Divide operator.
* <br><strong>Syntax:</strong> <code>x / y</code>
* <br><strong>Method:</strong> <code>T divide(L x, R y);</code>.
* @see JexlArithmetic#divide
*/
DIVIDE("/", "divide", 2),
/**
* Modulo operator.
* <br><strong>Syntax:</strong> <code>x % y</code>
* <br><strong>Method:</strong> <code>T mod(L x, R y);</code>.
* @see JexlArithmetic#mod
*/
MOD("%", "mod", 2),
/**
* Bitwise-and operator.
* <br><strong>Syntax:</strong> <code>x &amp; y</code>
* <br><strong>Method:</strong> <code>T and(L x, R y);</code>.
* @see JexlArithmetic#and
*/
AND("&", "and", 2),
/**
* Bitwise-or operator.
* <br><strong>Syntax:</strong> <code>x | y</code>
* <br><strong>Method:</strong> <code>T or(L x, R y);</code>.
* @see JexlArithmetic#or
*/
OR("|", "or", 2),
/**
* Bitwise-xor operator.
* <br><strong>Syntax:</strong> <code>x ^ y</code>
* <br><strong>Method:</strong> <code>T xor(L x, R y);</code>.
* @see JexlArithmetic#xor
*/
XOR("^", "xor", 2),
/**
* Equals operator.
* <br><strong>Syntax:</strong> <code>x == y</code>
* <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code>.
* @see JexlArithmetic#equals
*/
EQ("==", "equals", 2),
/**
* Less-than operator.
* <br><strong>Syntax:</strong> <code>x &lt; y</code>
* <br><strong>Method:</strong> <code>boolean lessThan(L x, R y);</code>.
* @see JexlArithmetic#lessThan
*/
LT("<", "lessThan", 2),
/**
* Less-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x &lt;= y</code>
* <br><strong>Method:</strong> <code>boolean lessThanOrEqual(L x, R y);</code>.
* @see JexlArithmetic#lessThanOrEqual
*/
LTE("<=", "lessThanOrEqual", 2),
/**
* Greater-than operator.
* <br><strong>Syntax:</strong> <code>x &gt; y</code>
* <br><strong>Method:</strong> <code>boolean greaterThan(L x, R y);</code>.
* @see JexlArithmetic#greaterThan
*/
GT(">", "greaterThan", 2),
/**
* Greater-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x &gt;= y</code>
* <br><strong>Method:</strong> <code>boolean greaterThanOrEqual(L x, R y);</code>.
* @see JexlArithmetic#greaterThanOrEqual
*/
GTE(">=", "greaterThanOrEqual", 2),
/**
* Contains operator.
* <br><strong>Syntax:</strong> <code>x =~ y</code>
* <br><strong>Method:</strong> <code>boolean contains(L x, R y);</code>.
* @see JexlArithmetic#contains
*/
CONTAINS("=~", "contains", 2),
/**
* Starts-with operator.
* <br><strong>Syntax:</strong> <code>x =^ y</code>
* <br><strong>Method:</strong> <code>boolean startsWith(L x, R y);</code>.
* @see JexlArithmetic#startsWith
*/
STARTSWITH("=^", "startsWith", 2),
/**
* Ends-with operator.
* <br><strong>Syntax:</strong> <code>x =$ y</code>
* <br><strong>Method:</strong> <code>boolean endsWith(L x, R y);</code>.
* @see JexlArithmetic#endsWith
*/
ENDSWITH("=$", "endsWith", 2),
/**
* Not operator.
* <br><strong>Syntax:</strong> <code>!x</code>
* <br><strong>Method:</strong> <code>T not(L x);</code>.
* @see JexlArithmetic#not
*/
NOT("!", "not", 1),
/**
* Complement operator.
* <br><strong>Syntax:</strong> <code>~x</code>
* <br><strong>Method:</strong> <code>T complement(L x);</code>.
* @see JexlArithmetic#complement
*/
COMPLEMENT("~", "complement", 1),
/**
* Negate operator.
* <br><strong>Syntax:</strong> <code>-x</code>
* <br><strong>Method:</strong> <code>T negate(L x);</code>.
* @see JexlArithmetic#negate
*/
NEGATE("-", "negate", 1),
/**
* Positivize operator.
* <br><strong>Syntax:</strong> <code>+x</code>
* <br><strong>Method:</strong> <code>T positivize(L x);</code>.
* @see JexlArithmetic#positivize
*/
POSITIVIZE("+", "positivize", 1),
/**
* Empty operator.
* <br><strong>Syntax:</strong> <code>empty x</code> or <code>empty(x)</code>
* <br><strong>Method:</strong> <code>boolean empty(L x);</code>.
* @see JexlArithmetic#empty
*/
EMPTY("empty", "empty", 1),
/**
* Size operator.
* <br><strong>Syntax:</strong> <code>size x</code> or <code>size(x)</code>
* <br><strong>Method:</strong> <code>int size(L x);</code>.
* @see JexlArithmetic#size
*/
SIZE("size", "size", 1),
/**
* Self-add operator.
* <br><strong>Syntax:</strong> <code>x += y</code>
* <br><strong>Method:</strong> <code>T selfAdd(L x, R y);</code>.
*/
SELF_ADD("+=", "selfAdd", ADD),
/**
* Self-subtract operator.
* <br><strong>Syntax:</strong> <code>x -= y</code>
* <br><strong>Method:</strong> <code>T selfSubtract(L x, R y);</code>.
*/
SELF_SUBTRACT("-=", "selfSubtract", SUBTRACT),
/**
* Self-multiply operator.
* <br><strong>Syntax:</strong> <code>x *= y</code>
* <br><strong>Method:</strong> <code>T selfMultiply(L x, R y);</code>.
*/
SELF_MULTIPLY("*=", "selfMultiply", MULTIPLY),
/**
* Self-divide operator.
* <br><strong>Syntax:</strong> <code>x /= y</code>
* <br><strong>Method:</strong> <code>T selfDivide(L x, R y);</code>.
*/
SELF_DIVIDE("/=", "selfDivide", DIVIDE),
/**
* Self-modulo operator.
* <br><strong>Syntax:</strong> <code>x %= y</code>
* <br><strong>Method:</strong> <code>T selfMod(L x, R y);</code>.
*/
SELF_MOD("%=", "selfMod", MOD),
/**
* Self-and operator.
* <br><strong>Syntax:</strong> <code>x &amp;= y</code>
* <br><strong>Method:</strong> <code>T selfAnd(L x, R y);</code>.
*/
SELF_AND("&=", "selfAnd", AND),
/**
* Self-or operator.
* <br><strong>Syntax:</strong> <code>x |= y</code>
* <br><strong>Method:</strong> <code>T selfOr(L x, R y);</code>.
*/
SELF_OR("|=", "selfOr", OR),
/**
* Self-xor operator.
* <br><strong>Syntax:</strong> <code>x ^= y</code>
* <br><strong>Method:</strong> <code>T selfXor(L x, R y);</code>.
*/
SELF_XOR("^=", "selfXor", XOR),
/**
* Marker for side effect.
* <br>Returns this from 'self*' overload method to let the engine know the side effect has been performed and
* there is no need to assign the result.
*/
ASSIGN("=", null, null),
/**
* Property get operator as in: x.y.
* <br><strong>Syntax:</strong> <code>x.y</code>
* <br><strong>Method:</strong> <code>Object propertyGet(L x, R y);</code>.
*/
PROPERTY_GET(".", "propertyGet", 2),
/**
* Property set operator as in: x.y = z.
* <br><strong>Syntax:</strong> <code>x.y = z</code>
* <br><strong>Method:</strong> <code>void propertySet(L x, R y, V z);</code>.
*/
PROPERTY_SET(".=", "propertySet", 3),
/**
* Array get operator as in: x[y].
* <br><strong>Syntax:</strong> <code>x.y</code>
* <br><strong>Method:</strong> <code>Object arrayGet(L x, R y);</code>.
*/
ARRAY_GET("[]", "arrayGet", 2),
/**
* Array set operator as in: x[y] = z.
* <br><strong>Syntax:</strong> <code>x[y] = z</code>
* <br><strong>Method:</strong> <code>void arraySet(L x, R y, V z);</code>.
*/
ARRAY_SET("[]=", "arraySet", 3),
/**
* Iterator generator as in for(var x : y).
* If the returned Iterator is AutoCloseable, close will be called after the last execution of the loop block.
* <br><strong>Syntax:</strong> <code>for(var x : y){...} </code>
* <br><strong>Method:</strong> <code>Iterator&lt;Object&gt; forEach(R y);</code>.
* @since 3.1
*/
FOR_EACH("for(...)", "forEach", 1);
/**
* The operator symbol.
*/
private final String operator;
/**
* The associated operator method name.
*/
private final String methodName;
/**
* The method arity (ie number of arguments).
*/
private final int arity;
/**
* The base operator.
*/
private final JexlOperator base;
/**
* Creates a base operator.
*
* @param o the operator name
* @param m the method name associated to this operator in a JexlArithmetic
* @param argc the number of parameters for the method
*/
JexlOperator(final String o, final String m, final int argc) {
this.operator = o;
this.methodName = m;
this.arity = argc;
this.base = null;
}
/**
* Creates a side-effect operator.
*
* @param o the operator name
* @param m the method name associated to this operator in a JexlArithmetic
* @param b the base operator, ie + for +=
*/
JexlOperator(final String o, final String m, final JexlOperator b) {
this.operator = o;
this.methodName = m;
this.arity = 2;
this.base = b;
}
/**
* Gets this operator symbol.
*
* @return the symbol
*/
public final String getOperatorSymbol() {
return operator;
}
/**
* Gets this operator method name in a JexlArithmetic.
*
* @return the method name
*/
public final String getMethodName() {
return methodName;
}
/**
* Gets this operator number of parameters.
*
* @return the method arity
*/
public int getArity() {
return arity;
}
/**
* Gets the base operator.
*
* @return the base operator
*/
public final JexlOperator getBaseOperator() {
return base;
}
}

View File

@ -0,0 +1,411 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.math.MathContext;
import java.util.Collections;
import java.util.Map;
import aiyh.utils.tool.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);
}
}

View File

@ -0,0 +1,163 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* A JEXL Script.
*
* <p>A script is some valid JEXL syntax to be executed with a given set of {@link JexlContext} variables.</p>
*
* <p>A script is a group of statements, separated by semicolons.</p>
*
* <p>The statements can be <code>blocks</code> (curly braces containing code),
* Control statements such as <code>if</code> and <code>while</code>
* as well as expressions and assignment statements.</p>
*
* <p>Do <em>not</em> create classes that implement this interface; delegate or compose instead.</p>
*
* @since 1.1
*/
public interface JexlScript {
/**
* Returns the source text of this expression.
*
* @return the source text
*/
String getSourceText();
/**
* Recreates the source text of this expression from the internal syntactic tree.
*
* @return the source text
*/
String getParsedText();
/**
* Recreates the source text of this expression from the internal syntactic tree.
*
* @param indent the number of spaces for indentation, 0 meaning no indentation
* @return the source text
*/
String getParsedText(int indent);
/**
* Executes the script with the variables contained in the
* supplied {@link JexlContext}.
*
* @param context A JexlContext containing variables.
* @return The result of this script, usually the result of
* the last statement.
*/
Object execute(JexlContext context);
/**
* Executes the script with the variables contained in the
* supplied {@link JexlContext} and a set of arguments corresponding to the
* parameters used during parsing.
*
* @param context A JexlContext containing variables.
* @param args the arguments
* @return The result of this script, usually the result of
* the last statement.
* @since 2.1
*/
Object execute(JexlContext context, Object... args);
/**
* Gets this script parameters.
*
* @return the parameters or null
* @since 2.1
*/
String[] getParameters();
/**
* Gets this script unbound parameters.
* <p>Parameters that haven't been bound by a previous call to curry().</p>
* @return the parameters or null
* @since 3.2
*/
String[] getUnboundParameters();
/**
* Gets this script local variables.
*
* @return the local variables or null
* @since 2.1
*/
String[] getLocalVariables();
/**
* Gets this script variables.
* <p>Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as
* a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.</p>
*
* @return the variables or null
* @since 2.1
*/
Set<List<String>> getVariables();
/**
* Gets this script pragmas.
*
* @return the (non null, may be empty) pragmas map
*/
Map<String, Object> getPragmas();
/**
* Creates a Callable from this script.
*
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
*
* @param context the context
* @return the callable
* @since 2.1
*/
Callable<Object> callable(JexlContext context);
/**
* Creates a Callable from this script.
*
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
*
* @param context the context
* @param args the script arguments
* @return the callable
* @since 2.1
*/
Callable<Object> callable(JexlContext context, Object... args);
/**
* Curries this script, returning a script with bound arguments.
*
* <p>If this script does not declare parameters or if all of them are already bound,
* no error is generated and this script is returned.</p>
*
* @param args the arguments to bind
* @return the curried script or this script if no binding can occur
*/
JexlScript curry(Object... args);
}

View File

@ -0,0 +1,417 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A simple "JeXL Template" engine.
*
* <p>At the base is an evaluator similar to the Unified EL evaluator used in JSP/JSF based on JEXL.
* At the top is a template engine inspired by Velocity that uses JEXL (instead of OGNL/VTL) as the scripting
* language.</p>
*
* <p>The evaluator is intended to be used in configuration modules, XML based frameworks or JSP taglibs
* and facilitate the implementation of expression evaluation.</p>
*
* <p>The template engine is intended to output any form of text; html, XML, CSV...</p>
*
* @since 3.0
*/
public abstract class JxltEngine {
/**
* The sole type of (runtime) exception the JxltEngine can throw.
*/
public static class Exception extends JexlException {
/** Serial version UID. */
private static final long serialVersionUID = 201112030113L;
/**
* Creates an Exception.
*
* @param info the contextual information
* @param msg the exception message
* @param cause the exception cause
*/
public Exception(final JexlInfo info, final String msg, final Throwable cause) {
super(info, msg, cause);
}
}
/**
* A unified expression that can mix immediate, deferred and nested sub-expressions as well as string constants;
* <ul>
* <li>The "immediate" syntax is of the form <code>"...${jexl-expr}..."</code></li>
* <li>The "deferred" syntax is of the form <code>"...#{jexl-expr}..."</code></li>
* <li>The "nested" syntax is of the form <code>"...#{...${jexl-expr0}...}..."</code></li>
* <li>The "composite" syntax is of the form <code>"...${jexl-expr0}... #{jexl-expr1}..."</code></li>
* </ul>
*
* <p>Deferred and immediate expression carry different intentions:</p>
*
* <ul>
* <li>An immediate expression indicate that evaluation is intended to be performed close to
* the definition/parsing point.</li>
* <li>A deferred expression indicate that evaluation is intended to occur at a later stage.</li>
* </ul>
*
* <p>For instance: <code>"Hello ${name}, now is #{time}"</code> is a composite "deferred" expression since one
* of its subexpressions is deferred. Furthermore, this (composite) expression intent is
* to perform two evaluations; one close to its definition and another one in a later
* phase.</p>
*
* <p>The API reflects this feature in 2 methods, prepare and evaluate. The prepare method
* will evaluate the immediate subexpression and return an expression that contains only
* the deferred subexpressions (and constants), a prepared expression. Such a prepared expression
* is suitable for a later phase evaluation that may occur with a different JexlContext.
* Note that it is valid to call evaluate without prepare in which case the same JexlContext
* is used for the 2 evaluation phases.</p>
*
* <p>In the most common use-case where deferred expressions are to be kept around as properties of objects,
* one should createExpression and prepare an expression before storing it and evaluate it each time
* the property storing it is accessed.</p>
*
* <p>Note that nested expression use the JEXL syntax as in:</p>
*
* <blockquote><code>"#{${bar}+'.charAt(2)'}"</code></blockquote>
*
* <p>The most common mistake leading to an invalid expression being the following:</p>
*
* <blockquote><code>"#{${bar}charAt(2)}"</code></blockquote>
*
* <p>Also note that methods that createExpression evaluate expressions may throw <em>unchecked</em> exceptions;
* The {@link Exception} are thrown when the engine instance is in "non-silent" mode
* but since these are RuntimeException, user-code <em>should</em> catch them where appropriate.</p>
*
* @since 2.0
*/
public interface Expression {
/**
* Generates this expression's string representation.
*
* @return the string representation
*/
String asString();
/**
* Adds this expression's string representation to a StringBuilder.
*
* @param strb the builder to fill
* @return the builder argument
*/
StringBuilder asString(StringBuilder strb);
/**
* Evaluates this expression.
*
* <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warning.</p>
*
* @param context the variable context
* @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is
* running in silent mode
* @throws Exception if an error occurs and the {@link JexlEngine}
* is not silent
*/
Object evaluate(JexlContext context);
/**
* Retrieves this expression's source expression.
* <p>
* If this expression was prepared, this allows to retrieve the
* original expression that lead to it.</p>
* <p>Other expressions return themselves.</p>
*
* @return the source expression
*/
Expression getSource();
/**
* Gets the list of variables accessed by this expression.
* <p>This method will visit all nodes of the sub-expressions and extract all variables whether they
* are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
*
* @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string)
* or the empty set if no variables are used
*/
Set<List<String>> getVariables();
/**
* Checks whether this expression is deferred.
*
* @return true if deferred, false otherwise
*/
boolean isDeferred();
/**
* Checks whether this expression is immediate.
*
* @return true if immediate, false otherwise
*/
boolean isImmediate();
/**
* Evaluates the immediate sub-expressions.
*
* <p>When the expression is dependant upon immediate and deferred sub-expressions,
* evaluates the immediate sub-expressions with the context passed as parameter
* and returns this expression deferred form.</p>
*
* <p>In effect, this binds the result of the immediate sub-expressions evaluation in the
* context, allowing to differ evaluation of the remaining (deferred) expression within another context.
* This only has an effect to nested and composite expressions that contain differed and
* immediate sub-expressions.</p>
*
* <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warning.* </p>
*
* @param context the context to use for immediate expression evaluations
* @return an {@link Expression} or null if an error occurs and the {@link JexlEngine} is running
* in silent mode
* @throws Exception if an error occurs and the {@link JexlEngine} is not in silent mode
*/
Expression prepare(JexlContext context);
/**
* Formats this expression, adding its source string representation in
* comments if available: 'expression /*= source *\/'' .
*
* @return the formatted expression string
*/
@Override
String toString();
}
/**
* Creates a a {@link Expression} from an expression string.
* Uses and fills up the expression cache if any.
*
* <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.</p>
*
* @param expression the {@link Template} string expression
* @return the {@link Expression}, null if silent and an error occurred
* @throws Exception if an error occurs and the {@link JexlEngine} is not silent
*/
public Expression createExpression(final String expression) {
return createExpression(null, expression);
}
/**
* Creates a a {@link Expression} from an expression string.
* Uses and fills up the expression cache if any.
*
* <p>If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.</p>
*
* @param info the {@link JexlInfo} source information
* @param expression the {@link Template} string expression
* @return the {@link Expression}, null if silent and an error occurred
* @throws Exception if an error occurs and the {@link JexlEngine} is not silent
*/
public abstract Expression createExpression(JexlInfo info, String expression);
/**
* A template is a JEXL script that evaluates by writing its content through a Writer.
* <p>
* The source text is parsed considering each line beginning with '$$' (as default pattern) as JEXL script code
* and all others as Unified JEXL expressions; those expressions will be invoked from the script during
* evaluation and their output gathered through a writer.
* It is thus possible to use looping or conditional construct "around" expressions generating output.
* </p>
* For instance:
* <blockquote><pre>
* $$ for(var x : [1, 3, 5, 42, 169]) {
* $$ if (x == 42) {
* Life, the universe, and everything
* $$ } else if (x &gt; 42) {
* The value $(x} is over forty-two
* $$ } else {
* The value ${x} is under forty-two
* $$ }
* $$ }
* </pre></blockquote>
*
* <p>Will evaluate as:</p>
*
* <blockquote><pre>
* The value 1 is under forty-two
* The value 3 is under forty-two
* The value 5 is under forty-two
* Life, the universe, and everything
* The value 169 is over forty-two
* </pre></blockquote>
*
* <p>During evaluation, the template context exposes its writer as '$jexl' which is safe to use in this case.
* This allows writing directly through the writer without adding new-lines as in:</p>
*
* <blockquote><pre>
* $$ for(var cell : cells) { $jexl.print(cell); $jexl.print(';') }
* </pre></blockquote>
*
* <p>A template is expanded as one JEXL script and a list of template expressions; each template expression is
* being replaced in the script by a call to jexl:print(expr) (the expr is in fact the expr number in the template).
* This integration uses a specialized JexlContext (TemplateContext) that serves as a namespace (for jexl:)
* and stores the template expression array and the writer (java.io.Writer) that the 'jexl:print(...)'
* delegates the output generation to.</p>
*
* @since 3.0
*/
public interface Template {
/**
* Recreate the template source from its inner components.
*
* @return the template source rewritten
*/
String asString();
/**
* Evaluates this template.
*
* @param context the context to use during evaluation
* @param writer the writer to use for output
*/
void evaluate(JexlContext context, Writer writer);
/**
* Evaluates this template.
*
* @param context the context to use during evaluation
* @param writer the writer to use for output
* @param args the arguments
*/
void evaluate(JexlContext context, Writer writer, Object... args);
/**
* Prepares this template by expanding any contained deferred TemplateExpression.
*
* @param context the context to prepare against
* @return the prepared version of the template
*/
Template prepare(JexlContext context);
/**
* Gets the list of variables accessed by this template.
* <p>This method will visit all nodes of the sub-expressions and extract all variables whether they
* are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
*
* @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string)
* or the empty set if no variables are used
*/
Set<List<String>> getVariables();
/**
* Gets the list of parameters expected by this template.
*
* @return the parameter names array
*/
String[] getParameters();
/**
* Gets this script pragmas.
*
* @return the (non null, may be empty) pragmas map
* @since 3.1
*/
Map<String, Object> getPragmas();
}
/**
* Creates a new template.
*
* @param info the jexl info (file, line, column)
* @param prefix the directive prefix
* @param source the source
* @param parms the parameter names
* @return the template
*/
public abstract Template createTemplate(JexlInfo info, String prefix, Reader source, String... parms);
/**
* Creates a new template.
*
* @param info the source info
* @param parms the parameter names
* @param source the source
* @return the template
*/
public Template createTemplate(final JexlInfo info, final String source, final String... parms) {
return createTemplate(info, "$$", new StringReader(source), parms);
}
/**
* Creates a new template.
*
* @param info the source info
* @param source the source
* @return the template
*/
public Template createTemplate(final JexlInfo info, final String source) {
return createTemplate(info, "$$", new StringReader(source), (String[]) null);
}
/**
* Creates a new template.
*
* @param prefix the directive prefix
* @param source the source
* @param parms the parameter names
* @return the template
*/
public Template createTemplate(final String prefix, final Reader source, final String... parms) {
return createTemplate(null, prefix, source, parms);
}
/**
* Creates a new template.
*
* @param source the source
* @param parms the parameter names
* @return the template
*/
public Template createTemplate(final String source, final String... parms) {
return createTemplate(null, source, parms);
}
/**
* Creates a new template.
*
* @param source the source
* @return the template
*/
public Template createTemplate(final String source) {
return createTemplate(null, source);
}
/**
* Gets the {@link JexlEngine} underlying this template engine.
*
* @return the JexlEngine
*/
public abstract JexlEngine getEngine();
/**
* Clears the cache.
*/
public abstract void clearCache();
}

View File

@ -0,0 +1,71 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import java.util.HashMap;
import java.util.Map;
/**
* Wraps a map in a context.
* <p>Each entry in the map is considered a variable name, value pair.</p>
*/
public class MapContext implements JexlContext {
/**
* The wrapped variable map.
*/
private final Map<String, Object> map;
/**
* Creates a MapContext on an automatically allocated underlying HashMap.
*/
public MapContext() {
this(null);
}
/**
* Creates a MapContext wrapping an existing user provided map.
*
* @param vars the variable map
*/
public MapContext(final Map<String, Object> vars) {
map = vars == null ? new HashMap<String, Object>() : vars;
}
@Override
public boolean has(final String name) {
return map.containsKey(name);
}
@Override
public Object get(final String name) {
return map.get(name);
}
@Override
public void set(final String name, final Object value) {
map.put(name, value);
}
/**
* Clears all variables.
*/
public void clear() {
map.clear();
}
}

View File

@ -0,0 +1,108 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
/**
* Wraps an Object as a JEXL context and NamespaceResolver.
*
* @param <T> the wrapped object type to use
* @since 3.0
*/
public class ObjectContext<T> implements JexlContext, JexlContext.NamespaceResolver {
/** The property solving jexl engine. */
private final JexlEngine jexl;
/** The object serving as context provider. */
private final T object;
/**
* @return the Jexl engine
*/
protected JexlEngine getJexl() {
return jexl;
}
/**
* @return the object exposed by this context
*/
protected T getObject() {
return object;
}
/**
* Creates a new ObjectContext.
*
* @param engine the jexl engine to use to solve properties
* @param wrapped the object to wrap in this context
*/
public ObjectContext(final JexlEngine engine, final T wrapped) {
this.jexl = engine;
this.object = wrapped;
}
@Override
public Object get(final String name) {
final JexlPropertyGet jget = jexl.getUberspect().getPropertyGet(object, name);
if (jget != null) {
try {
return jget.invoke(object);
} catch (final Exception xany) {
if (jexl.isStrict()) {
throw new JexlException.Property(null, name, true, xany);
}
}
}
return null;
}
@Override
public void set(final String name, final Object value) {
final JexlPropertySet jset = jexl.getUberspect().getPropertySet(object, name, value);
if (jset != null) {
try {
jset.invoke(object, value);
} catch (final Exception xany) {
// ignore
if (jexl.isStrict()) {
throw new JexlException.Property(null, name, true, xany);
}
}
}
}
@Override
public boolean has(final String name) {
final JexlPropertyGet jget = jexl.getUberspect().getPropertyGet(object, name);
try {
return jget != null && jget.invoke(object) != null;
} catch (final Exception xany) {
return false;
}
}
@Override
public Object resolveNamespace(final String name) {
if (name == null || name.isEmpty()) {
return object;
}
return null;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.annotations;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates JEXL Introspection should not see this element.
* <p>
* This allows to completely hide a package, class, interface, constructor, method or field from
* JEXL; a NoJexl annotated element will not be usable through any kind of JEXL expression or script.
* </p>
* See {@link JexlSandbox} for another way to restrict JEXL access.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.PACKAGE})
public @interface NoJexl {
}

View File

@ -0,0 +1,27 @@
<html>
<!--
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.
-->
<head>
<title>Package Documentation for org.apache.commons.jexl3.annotations Package</title>
</head>
<body>
<h2>Provides annotation for introspection services.</h2>
<p>The only annotation in this package allows to restrict what JEXL
can introspect and expose through scripting.
</p>
</body>
</html>

View File

@ -0,0 +1,139 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
/**
* Helper class to create typed arrays.
*/
public class ArrayBuilder implements JexlArithmetic.ArrayBuilder {
/** The number of primitive types. */
private static final int PRIMITIVE_SIZE = 8;
/** The boxing types to primitive conversion map. */
private static final Map<Class<?>, Class<?>> BOXING_CLASSES;
static {
BOXING_CLASSES = new IdentityHashMap<Class<?>, Class<?>>(PRIMITIVE_SIZE);
BOXING_CLASSES.put(Boolean.class, Boolean.TYPE);
BOXING_CLASSES.put(Byte.class, Byte.TYPE);
BOXING_CLASSES.put(Character.class, Character.TYPE);
BOXING_CLASSES.put(Double.class, Double.TYPE);
BOXING_CLASSES.put(Float.class, Float.TYPE);
BOXING_CLASSES.put(Integer.class, Integer.TYPE);
BOXING_CLASSES.put(Long.class, Long.TYPE);
BOXING_CLASSES.put(Short.class, Short.TYPE);
}
/**
* Gets the primitive type of a given class (when it exists).
* @param parm a class
* @return the primitive type or null it the argument is not unboxable
*/
protected static Class<?> unboxingClass(final Class<?> parm) {
final Class<?> prim = BOXING_CLASSES.get(parm);
return prim == null ? parm : prim;
}
/** The intended class array. */
protected Class<?> commonClass = null;
/** Whether the array stores numbers. */
protected boolean isNumber = true;
/** Whether we can try unboxing. */
protected boolean unboxing = true;
/** The untyped list of items being added. */
protected final Object[] untyped;
/** Number of added items. */
protected int added = 0;
/**
* Creates a new builder.
* @param size the exact array size
*/
public ArrayBuilder(final int size) {
untyped = new Object[size];
}
@Override
public void add(final Object value) {
// for all children after first...
if (!Object.class.equals(commonClass)) {
if (value == null) {
isNumber = false;
unboxing = false;
} else {
Class<?> eclass = value.getClass();
// base common class on first non-null entry
if (commonClass == null) {
commonClass = eclass;
isNumber = isNumber && Number.class.isAssignableFrom(commonClass);
} else if (!commonClass.equals(eclass)) {
// if both are numbers...
if (isNumber && Number.class.isAssignableFrom(eclass)) {
commonClass = Number.class;
} else {
// attempt to find valid superclass
do {
eclass = eclass.getSuperclass();
if (eclass == null) {
commonClass = Object.class;
break;
}
} while (!commonClass.isAssignableFrom(eclass));
}
}
}
}
if (added >= untyped.length) {
throw new IllegalArgumentException("add() over size");
}
untyped[added++] = value;
}
@Override
public Object create(final boolean extended) {
if (untyped == null) {
return new Object[0];
}
if (extended) {
final List<Object> list = new ArrayList<Object>(added);
list.addAll(Arrays.asList(untyped).subList(0, added));
return list;
}
// convert untyped array to the common class if not Object.class
if ((commonClass == null) || Object.class.equals(commonClass)) {
return untyped.clone();
}
final int size = added;
// if the commonClass is a number, it has an equivalent primitive type, get it
if (unboxing) {
commonClass = unboxingClass(commonClass);
}
// allocate and fill up the typed array
final Object typed = Array.newInstance(commonClass, size);
for (int i = 0; i < size; ++i) {
Array.set(typed, i, untyped[i]);
}
return typed;
}
}

View File

@ -0,0 +1,140 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlLambda;
import java.util.Objects;
/**
* A Script closure.
*/
public class Closure extends Script {
/** The frame. */
protected final Frame frame;
/**
* Creates a closure.
* @param theCaller the calling interpreter
* @param lambda the lambda
*/
protected Closure(final Interpreter theCaller, final ASTJexlLambda lambda) {
super(theCaller.jexl, null, lambda);
frame = lambda.createFrame(theCaller.frame);
}
/**
* Creates a curried version of a script.
* @param base the base script
* @param args the script arguments
*/
protected Closure(final Script base, final Object[] args) {
super(base.jexl, base.source, base.script);
final Frame sf = (base instanceof Closure) ? ((Closure) base).frame : null;
frame = sf == null
? script.createFrame(args)
: sf.assign(args);
}
@Override
public int hashCode() {
// CSOFF: Magic number
int hash = 17;
hash = 31 * hash + (this.jexl != null ? this.jexl.hashCode() : 0);
hash = 31 * hash + (this.source != null ? this.source.hashCode() : 0);
hash = 31 * hash + (this.frame != null ? this.frame.hashCode() : 0);
// CSON: Magic number
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Closure other = (Closure) obj;
if (this.jexl != other.jexl) {
return false;
}
if (!Objects.equals(this.source, other.source)) {
return false;
}
if (!Objects.equals(this.frame, other.frame)) {
return false;
}
return true;
}
@Override
public String[] getUnboundParameters() {
return frame.getUnboundParameters();
}
/**
* Sets the captured index of a given symbol, ie the target index of a parent
* captured symbol in this closure's frame.
* <p>This is meant to allow a locally defined function to "see" and call
* itself as a local (captured) variable;
* in other words, this allows recursive call of a function.
* @param symbol the symbol index (in the caller of this closure)
* @param value the value to set in the local frame
*/
public void setCaptured(final int symbol, final Object value) {
if (script instanceof ASTJexlLambda) {
final ASTJexlLambda lambda = (ASTJexlLambda) script;
final Scope scope = lambda.getScope();
if (scope != null) {
final Integer reg = scope.getCaptured(symbol);
if (reg != null) {
frame.set(reg, value);
}
}
}
}
@Override
public Object evaluate(final JexlContext context) {
return execute(context, (Object[])null);
}
@Override
public Object execute(final JexlContext context) {
return execute(context, (Object[])null);
}
@Override
public Object execute(final JexlContext context, final Object... args) {
final Frame local = frame != null? frame.assign(args) : null;
final Interpreter interpreter = createInterpreter(context, local);
return interpreter.runClosure(this, null);
}
@Override
public Callable callable(final JexlContext context, final Object... args) {
final Frame local = frame != null? frame.assign(args) : null;
return new Callable(createInterpreter(context, local)) {
@Override
public Object interpret() {
return interpreter.runClosure(Closure.this, null);
}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,925 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.*;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.SandboxUberspect;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.Uberspect;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
/**
* A JexlEngine implementation.
*
* @since 2.0
*/
public class Engine extends JexlEngine {
/**
* Gets the default instance of Uberspect.
* <p>This is lazily initialized to avoid building a default instance if there
* is no use for it. The main reason for not using the default Uberspect instance is to
* be able to use a (low level) introspector created with a given logger
* instead of the default one.</p>
* <p>Implemented as on demand holder idiom.</p>
*/
private static final class UberspectHolder {
/** The default uberspector that handles all introspection patterns. */
private static final Uberspect UBERSPECT =
new Uberspect(LogFactory.getLog(JexlEngine.class), JexlUberspect.JEXL_STRATEGY);
/** Non-instantiable. */
private UberspectHolder() {
}
}
/**
* The name of the options pragma.
*/
protected static final String PRAGMA_OPTIONS = "jexl.options";
/**
* The prefix of a namespace pragma.
*/
protected static final String PRAGMA_JEXLNS = "jexl.namespace.";
/**
* The Log to which all JexlEngine messages will be logged.
*/
protected final Log logger;
/**
* The JexlUberspect instance.
*/
protected final JexlUberspect uberspect;
/**
* The {@link JexlArithmetic} instance.
*/
protected final JexlArithmetic arithmetic;
/**
* The map of 'prefix:function' to object implementing the namespaces.
*/
protected final Map<String, Object> functions;
/**
* The maximum stack height.
*/
protected final int stackOverflow;
/**
* Whether this engine considers unknown variables, methods and constructors as errors.
*/
protected final boolean strict;
/**
* Whether this engine considers null in navigation expression as errors.
*/
protected final boolean safe;
/**
* Whether expressions evaluated by this engine will throw exceptions (false) or return null (true) on errors.
* Default is false.
*/
protected final boolean silent;
/**
* Whether expressions evaluated by this engine will throw JexlException.Cancel (true) or return null (false) when
* interrupted.
* Default is true when not silent and strict.
*/
protected final boolean cancellable;
/**
* Whether error messages will carry debugging information.
*/
protected final boolean debug;
/**
* The set of default script parsing features.
*/
protected final JexlFeatures scriptFeatures;
/**
* The set of default expression parsing features.
*/
protected final JexlFeatures expressionFeatures;
/**
* The default charset.
*/
protected final Charset charset;
/**
* The atomic parsing flag; true whilst parsing.
*/
protected final AtomicBoolean parsing = new AtomicBoolean(false);
/**
* The {@link Parser}; when parsing expressions, this engine uses the parser if it
* is not already in use otherwise it will create a new temporary one.
*/
protected final Parser parser = new Parser(new StringProvider(";")); //$NON-NLS-1$
/**
* The expression max length to hit the cache.
*/
protected final int cacheThreshold;
/**
* The expression cache.
*/
protected final SoftCache<Source, ASTJexlScript> cache;
/**
* The default jxlt engine.
*/
protected volatile TemplateEngine jxlt = null;
/**
* Collect all or only dot references.
*/
protected final int collectMode;
/**
* A cached version of the options.
*/
protected final JexlOptions options;
/**
* Creates an engine with default arguments.
*/
public Engine() {
this(new JexlBuilder());
}
/**
* Creates a JEXL engine using the provided {@link JexlBuilder}.
*
* @param conf the builder
*/
public Engine(final JexlBuilder conf) {
// options:
this.options = conf.options().copy();
this.strict = options.isStrict();
this.safe = options.isSafe();
this.silent = options.isSilent();
this.cancellable = option(conf.cancellable(), !silent && strict);
options.setCancellable(cancellable);
this.debug = option(conf.debug(), true);
this.collectMode = conf.collectMode();
this.stackOverflow = conf.stackOverflow() > 0 ? conf.stackOverflow() : Integer.MAX_VALUE;
// core properties:
final JexlUberspect uber = conf.uberspect() == null ? getUberspect(conf.logger(), conf.strategy()) : conf.uberspect();
final ClassLoader loader = conf.loader();
if (loader != null) {
uber.setClassLoader(loader);
}
final JexlSandbox sandbox = conf.sandbox();
if (sandbox == null) {
this.uberspect = uber;
} else {
this.uberspect = new SandboxUberspect(uber, sandbox);
}
this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger();
this.arithmetic = conf.arithmetic() == null ? new JexlArithmetic(this.strict) : conf.arithmetic();
options.setMathContext(arithmetic.getMathContext());
options.setMathScale(arithmetic.getMathScale());
options.setStrictArithmetic(arithmetic.isStrict());
this.functions = conf.namespaces() == null ? Collections.emptyMap() : conf.namespaces();
// parsing & features:
final JexlFeatures features = conf.features() == null ? DEFAULT_FEATURES : conf.features();
Predicate<String> nsTest = features.namespaceTest();
final Set<String> nsNames = functions.keySet();
if (!nsNames.isEmpty()) {
nsTest = nsTest == JexlFeatures.TEST_STR_FALSE ? nsNames::contains : nsTest.or(nsNames::contains);
}
this.expressionFeatures = new JexlFeatures(features).script(false).namespaceTest(nsTest);
this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest);
this.charset = conf.charset();
// caching:
this.cache = conf.cache() <= 0 ? null : new SoftCache<Source, ASTJexlScript>(conf.cache());
this.cacheThreshold = conf.cacheThreshold();
if (uberspect == null) {
throw new IllegalArgumentException("uberspect can not be null");
}
}
/**
* Gets the default instance of Uberspect.
* <p>This is lazily initialized to avoid building a default instance if there
* is no use for it. The main reason for not using the default Uberspect instance is to
* be able to use a (low level) introspector created with a given logger
* instead of the default one.</p>
*
* @param logger the logger to use for the underlying Uberspect
* @param strategy the property resolver strategy
* @return Uberspect the default uberspector instance.
*/
public static Uberspect getUberspect(final Log logger, final JexlUberspect.ResolverStrategy strategy) {
if ((logger == null || logger.equals(LogFactory.getLog(JexlEngine.class)))
&& (strategy == null || strategy == JexlUberspect.JEXL_STRATEGY)) {
return UberspectHolder.UBERSPECT;
}
return new Uberspect(logger, strategy);
}
@Override
public JexlUberspect getUberspect() {
return uberspect;
}
@Override
public JexlArithmetic getArithmetic() {
return arithmetic;
}
@Override
public boolean isDebug() {
return this.debug;
}
@Override
public boolean isSilent() {
return this.silent;
}
@Override
public boolean isStrict() {
return this.strict;
}
@Override
public boolean isCancellable() {
return this.cancellable;
}
@Override
public void setClassLoader(final ClassLoader loader) {
jxlt = null;
uberspect.setClassLoader(loader);
if (functions != null) {
final List<String> names = new ArrayList<String>(functions.keySet());
for (final String name : names) {
final Object functor = functions.get(name);
if (functor instanceof Class<?>) {
final Class<?> fclass = ((Class<?>) functor);
try {
final Class<?> nclass = loader.loadClass(fclass.getName());
if (nclass != fclass) {
functions.put(name, nclass);
}
} catch (final ClassNotFoundException xany) {
functions.put(name, fclass.getName());
}
}
}
}
if (cache != null) {
cache.clear();
}
}
@Override
public Charset getCharset() {
return charset;
}
/**
* Solves an optional option.
*
* @param conf the option as configured, may be null
* @param def the default value if null, shall not be null
* @param <T> the option type
* @return conf or def
*/
private static <T> T option(final T conf, final T def) {
return conf == null ? def : conf;
}
/**
* Extracts the engine evaluation options from context if available, the engine
* options otherwise.
* <p>If the context is a options handle and the handled options shared instance flag
* is false, this method creates a copy of the options making them immutable during execution.
*
* @param context the context
* @return the options if any
*/
protected JexlOptions options(final JexlContext context) {
// Make a copy of the handled options if any
if (context instanceof JexlContext.OptionsHandle) {
final JexlOptions jexlo = ((JexlContext.OptionsHandle) context).getEngineOptions();
if (jexlo != null) {
return jexlo.isSharedInstance() ? jexlo : jexlo.copy();
}
} else if (context instanceof Options) {
// This condition and block for compatibility between 3.1 and 3.2
final JexlOptions jexlo = options.copy();
final JexlEngine jexl = this;
final Options opts = (Options) context;
jexlo.setCancellable(option(opts.isCancellable(), jexl.isCancellable()));
jexlo.setSilent(option(opts.isSilent(), jexl.isSilent()));
jexlo.setStrict(option(opts.isStrict(), jexl.isStrict()));
final JexlArithmetic jexla = jexl.getArithmetic();
jexlo.setStrictArithmetic(option(opts.isStrictArithmetic(), jexla.isStrict()));
jexlo.setMathContext(opts.getArithmeticMathContext());
jexlo.setMathScale(opts.getArithmeticMathScale());
return jexlo;
}
return options;
}
/**
* Compute a script options for evaluation.
* <p>This calls processPragma(...).
*
* @param script the script
* @param context the context
* @return the options
*/
protected JexlOptions options(final ASTJexlScript script, final JexlContext context) {
final JexlOptions opts = options(context);
if (opts != options) {
// when feature lexical, try hard to run lexical
if (scriptFeatures.isLexical()) {
opts.setLexical(true);
}
if (scriptFeatures.isLexicalShade()) {
opts.setLexicalShade(true);
}
}
if (script != null) {
// process script pragmas if any
processPragmas(script, context, opts);
}
return opts;
}
/**
* Processes a script pragmas.
* <p>Only called from options(...)
*
* @param script the script
* @param context the context
* @param opts the options
*/
protected void processPragmas(final ASTJexlScript script, final JexlContext context, final JexlOptions opts) {
final Map<String, Object> pragmas = script.getPragmas();
if (pragmas != null && !pragmas.isEmpty()) {
final JexlContext.PragmaProcessor processor =
context instanceof JexlContext.PragmaProcessor
? (JexlContext.PragmaProcessor) context
: null;
Map<String, Object> ns = null;
for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
final String key = pragma.getKey();
final Object value = pragma.getValue();
if (value instanceof String) {
if (PRAGMA_OPTIONS.equals(key)) {
// jexl.options
final String[] vs = value.toString().split(" ");
opts.setFlags(vs);
} else if (key.startsWith(PRAGMA_JEXLNS)) {
// jexl.namespace.***
final String nsname = key.substring(PRAGMA_JEXLNS.length());
if (nsname != null && !nsname.isEmpty()) {
if (ns == null) {
ns = new HashMap<>(functions);
}
final String nsclass = value.toString();
try {
ns.put(nsname, uberspect.getClassLoader().loadClass(nsclass));
} catch (final ClassNotFoundException e) {
ns.put(nsname, nsclass);
}
}
}
}
if (processor != null) {
processor.processPragma(key, value);
}
}
if (ns != null) {
opts.setNamespaces(ns);
}
}
}
/**
* Sets options from this engine options.
*
* @param opts the options to set
* @return the options
*/
public JexlOptions optionsSet(final JexlOptions opts) {
if (opts != null) {
opts.set(options);
}
return opts;
}
@Override
public TemplateEngine createJxltEngine(final boolean noScript, final int cacheSize, final char immediate, final char deferred) {
return new TemplateEngine(this, noScript, cacheSize, immediate, deferred);
}
@Override
public void clearCache() {
if (cache != null) {
cache.clear();
}
}
/**
* Creates an interpreter.
*
* @param context a JexlContext; if null, the empty context is used instead.
* @param frame the interpreter frame
* @param opts the evaluation options
* @return an Interpreter
*/
protected Interpreter createInterpreter(final JexlContext context, final Frame frame, final JexlOptions opts) {
return new Interpreter(this, opts, context, frame);
}
@Override
public Script createExpression(final JexlInfo info, final String expression) {
return createScript(expressionFeatures, info, expression, null);
}
@Override
public Script createScript(final JexlFeatures features, final JexlInfo info, final String scriptText, final String... names) {
if (scriptText == null) {
throw new NullPointerException("source is null");
}
final String source = trimSource(scriptText);
final Scope scope = names == null || names.length == 0 ? null : new Scope(null, names);
final JexlFeatures ftrs = features == null ? scriptFeatures : features;
final ASTJexlScript tree = parse(info, ftrs, source, scope);
return new Script(this, source, tree);
}
/**
* The features allowed for property set/get methods.
*/
protected static final JexlFeatures PROPERTY_FEATURES = new JexlFeatures()
.localVar(false)
.loops(false)
.lambda(false)
.script(false)
.arrayReferenceExpr(false)
.methodCall(false)
.register(true);
@Override
public Object getProperty(final Object bean, final String expr) {
return getProperty(null, bean, expr);
}
@Override
public Object getProperty(JexlContext context, final Object bean, final String expr) {
if (context == null) {
context = EMPTY_CONTEXT;
}
// synthesize expr using register
String src = trimSource(expr);
src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src;
try {
final Scope scope = new Scope(null, "#0");
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean);
final Interpreter interpreter = createInterpreter(context, frame, options);
return interpreter.visitLexicalNode(node, null);
} catch (final JexlException xjexl) {
if (silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
return null;
}
throw xjexl.clean();
}
}
@Override
public void setProperty(final Object bean, final String expr, final Object value) {
setProperty(null, bean, expr, value);
}
@Override
public void setProperty(JexlContext context, final Object bean, final String expr, final Object value) {
if (context == null) {
context = EMPTY_CONTEXT;
}
// synthesize expr using register
String src = trimSource(expr);
src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=" + "#1";
try {
final Scope scope = new Scope(null, "#0", "#1");
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean, value);
final Interpreter interpreter = createInterpreter(context, frame, options);
interpreter.visitLexicalNode(node, null);
} catch (final JexlException xjexl) {
if (silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
return;
}
throw xjexl.clean();
}
}
@Override
public Object invokeMethod(final Object obj, final String meth, final Object... args) {
JexlException xjexl = null;
Object result = null;
final JexlInfo info = debug ? createInfo() : null;
try {
JexlMethod method = uberspect.getMethod(obj, meth, args);
if (method == null && arithmetic.narrowArguments(args)) {
method = uberspect.getMethod(obj, meth, args);
}
if (method != null) {
result = method.invoke(obj, args);
} else {
xjexl = new JexlException.Method(info, meth, args);
}
} catch (final JexlException xany) {
xjexl = xany;
} catch (final Exception xany) {
xjexl = new JexlException.Method(info, meth, args, xany);
}
if (xjexl != null) {
if (!silent) {
throw xjexl.clean();
}
logger.warn(xjexl.getMessage(), xjexl.getCause());
result = null;
}
return result;
}
@Override
public <T> T newInstance(final Class<? extends T> clazz, final Object... args) {
return clazz.cast(doCreateInstance(clazz, args));
}
@Override
public Object newInstance(final String clazz, final Object... args) {
return doCreateInstance(clazz, args);
}
/**
* Creates a new instance of an object using the most appropriate constructor
* based on the arguments.
*
* @param clazz the class to instantiate
* @param args the constructor arguments
* @return the created object instance or null on failure when silent
*/
protected Object doCreateInstance(final Object clazz, final Object... args) {
JexlException xjexl = null;
Object result = null;
final JexlInfo info = debug ? createInfo() : null;
try {
JexlMethod ctor = uberspect.getConstructor(clazz, args);
if (ctor == null && arithmetic.narrowArguments(args)) {
ctor = uberspect.getConstructor(clazz, args);
}
if (ctor != null) {
result = ctor.invoke(clazz, args);
} else {
xjexl = new JexlException.Method(info, clazz.toString(), args);
}
} catch (final JexlException xany) {
xjexl = xany;
} catch (final Exception xany) {
xjexl = new JexlException.Method(info, clazz.toString(), args, xany);
}
if (xjexl != null) {
if (silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
return null;
}
throw xjexl.clean();
}
return result;
}
/**
* Swaps the current thread local context.
*
* @param tls the context or null
* @return the previous thread local context
*/
protected JexlContext.ThreadLocal putThreadLocal(final JexlContext.ThreadLocal tls) {
final JexlContext.ThreadLocal local = CONTEXT.get();
CONTEXT.set(tls);
return local;
}
/**
* Swaps the current thread local engine.
*
* @param jexl the engine or null
* @return the previous thread local engine
*/
protected JexlEngine putThreadEngine(final JexlEngine jexl) {
final JexlEngine pjexl = ENGINE.get();
ENGINE.set(jexl);
return pjexl;
}
/**
* Gets the list of variables accessed by a script.
* <p>This method will visit all nodes of a script and extract all variables whether they
* are written in 'dot' or 'bracketed' notation. (a.b is equivalent to a['b']).</p>
*
* @param script the script
* @return the set of variables, each as a list of strings (ant-ish variables use more than 1 string)
* or the empty set if no variables are used
*/
protected Set<List<String>> getVariables(final ASTJexlScript script) {
final VarCollector collector = varCollector();
getVariables(script, script, collector);
return collector.collected();
}
/**
* Creates a collector instance.
*
* @return a collector instance
*/
protected VarCollector varCollector() {
return new VarCollector(this.collectMode);
}
/**
* Utility class to collect variables.
*/
protected static class VarCollector {
/**
* The collected variables represented as a set of list of strings.
*/
private final Set<List<String>> refs = new LinkedHashSet<List<String>>();
/**
* The current variable being collected.
*/
private List<String> ref = new ArrayList<String>();
/**
* The node that started the collect.
*/
private JexlNode root = null;
/**
* Whether constant array-access is considered equivalent to dot-access;
* if so, > 1 means collect any constant (set,map,...) instead of just
* strings and numbers.
*/
private int mode = 1;
/**
* Constructor.
*
* @param constaa whether constant array-access is considered equivalent to dot-access
*/
protected VarCollector(final int constaa) {
mode = constaa;
}
/**
* Starts/stops a variable collect.
*
* @param node starts if not null, stop if null
*/
public void collect(final JexlNode node) {
if (!ref.isEmpty()) {
refs.add(ref);
ref = new ArrayList<String>();
}
root = node;
}
/**
* @return true if currently collecting a variable, false otherwise
*/
public boolean isCollecting() {
return root instanceof ASTIdentifier;
}
/**
* Adds a 'segment' to the variable being collected.
*
* @param name the name
*/
public void add(final String name) {
ref.add(name);
}
/**
* @return the collected variables
*/
public Set<List<String>> collected() {
return refs;
}
}
/**
* Fills up the list of variables accessed by a node.
*
* @param script the owning script
* @param node the node
* @param collector the variable collector
*/
protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) {
if (node instanceof ASTIdentifier) {
final JexlNode parent = node.jjtGetParent();
if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
// skip identifiers for methods and functions
collector.collect(null);
return;
}
final ASTIdentifier identifier = (ASTIdentifier) node;
final int symbol = identifier.getSymbol();
// symbols that are captured are considered "global" variables
if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) {
collector.collect(null);
} else {
// start collecting from identifier
collector.collect(identifier);
collector.add(identifier.getName());
}
} else if (node instanceof ASTIdentifierAccess) {
final JexlNode parent = node.jjtGetParent();
if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
// skip identifiers for methods and functions
collector.collect(null);
return;
}
// belt and suspender since an identifier should have been seen first
if (collector.isCollecting()) {
collector.add(((ASTIdentifierAccess) node).getName());
}
} else if (node instanceof ASTArrayAccess && collector.mode > 0) {
final int num = node.jjtGetNumChildren();
// collect only if array access is const and follows an identifier
boolean collecting = collector.isCollecting();
for (int i = 0; i < num; ++i) {
final JexlNode child = node.jjtGetChild(i);
if (collecting && child.isConstant()) {
// collect all constants or only string and number literals
final boolean collect = collector.mode > 1
|| (child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral);
if (collect) {
final String image = child.toString();
collector.add(image);
}
} else {
collecting = false;
collector.collect(null);
getVariables(script, child, collector);
collector.collect(null);
}
}
} else {
final int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
getVariables(script, node.jjtGetChild(i), collector);
}
collector.collect(null);
}
}
/**
* Gets the array of parameters from a script.
*
* @param script the script
* @return the parameters which may be empty (but not null) if no parameters were defined
* @since 3.0
*/
protected String[] getParameters(final JexlScript script) {
return script.getParameters();
}
/**
* Gets the array of local variable from a script.
*
* @param script the script
* @return the local variables array which may be empty (but not null) if no local variables were defined
* @since 3.0
*/
protected String[] getLocalVariables(final JexlScript script) {
return script.getLocalVariables();
}
/**
* Parses an expression.
*
* @param info information structure
* @param expr whether we parse an expression or a feature
* @param src the expression to parse
* @param scope the script frame
* @return the parsed tree
* @throws JexlException if any error occurred during parsing
*/
protected ASTJexlScript parse(final JexlInfo info, final boolean expr, final String src, final Scope scope) {
return parse(info, expr ? this.expressionFeatures : this.scriptFeatures, src, scope);
}
/**
* Parses an expression.
*
* @param info information structure
* @param parsingf the set of parsing features
* @param src the expression to parse
* @param scope the script frame
* @return the parsed tree
* @throws JexlException if any error occurred during parsing
*/
protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) {
final boolean cached = src.length() < cacheThreshold && cache != null;
JexlFeatures features = parsingf != null ? parsingf : DEFAULT_FEATURES;
// if (features.getNameSpaces().isEmpty() && !functions.isEmpty()) {
// features = new JexlFeatures(features).nameSpaces(functions.keySet());
// }
final Source source = cached ? new Source(features, src) : null;
ASTJexlScript script = null;
if (source != null) {
script = cache.get(source);
if (script != null) {
final Scope f = script.getScope();
if ((f == null && scope == null) || (f != null && f.equals(scope))) {
return script;
}
}
}
final JexlInfo ninfo = info == null && debug ? createInfo() : info;
// if parser not in use...
if (parsing.compareAndSet(false, true)) {
try {
// lets parse
script = parser.parse(ninfo, features, src, scope);
} finally {
// no longer in use
parsing.set(false);
}
} else {
// ...otherwise parser was in use, create a new temporary one
final Parser lparser = new Parser(new StringProvider(";"));
script = lparser.parse(ninfo, features, src, scope);
}
if (source != null) {
cache.put(source, script);
}
return script;
}
/**
* Trims the source from front and ending spaces.
*
* @param str expression to clean
* @return trimmed expression ending in a semicolon
*/
protected String trimSource(final CharSequence str) {
if (str != null) {
int start = 0;
int end = str.length();
if (end > 0) {
// trim front spaces
while (start < end && Character.isSpaceChar(str.charAt(start))) {
++start;
}
// trim ending spaces; end is > 0 since start >= 0
while (end > start && Character.isSpaceChar(str.charAt(end - 1))) {
--end;
}
return str.subSequence(start, end).toString();
}
return "";
}
return null;
}
/**
* Gets and/or creates a default template engine.
*
* @return a template engine
*/
protected TemplateEngine jxlt() {
TemplateEngine e = jxlt;
if (e == null) {
synchronized (this) {
e = jxlt;
if (e == null) {
e = new TemplateEngine(this, true, 0, '$', '#');
jxlt = e;
}
}
}
return e;
}
}

View File

@ -0,0 +1,126 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.util.Arrays;
/**
* A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic).
* @since 3.0
*/
public final class Frame {
/** The scope. */
private final Scope scope;
/** The actual stack frame. */
private final Object[] stack;
/** Number of curried parameters. */
private final int curried;
/**
* Creates a new frame.
* @param s the scope
* @param r the stack frame
* @param c the number of curried parameters
*/
Frame(final Scope s, final Object[] r, final int c) {
scope = s;
stack = r;
curried = c;
}
/**
* Gets this script unbound parameters, i.e. parameters not bound through curry().
* @return the parameter names
*/
public String[] getUnboundParameters() {
return scope.getParameters(curried);
}
/**
* Gets the scope.
* @return this frame scope
*/
public Scope getScope() {
return scope;
}
@Override
public int hashCode() {
return Arrays.deepHashCode(this.stack);
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Frame other = (Frame) obj;
return Arrays.deepEquals(this.stack, other.stack);
}
/**
* Gets a value.
* @param s the offset in this frame
* @return the stacked value
*/
Object get(final int s) {
return stack[s];
}
/**
* Whether this frame defines a symbol, ie declared it and assigned it a value.
* @param s the offset in this frame
* @return true if this symbol has been assigned a value, false otherwise
*/
boolean has(final int s) {
return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED;
}
/**
* Sets a value.
* @param r the offset in this frame
* @param value the value to set in this frame
*/
void set(final int r, final Object value) {
stack[r] = value;
}
/**
* Assign values to this frame.
* @param values the values
* @return this frame
*/
Frame assign(final Object... values) {
if (stack != null) {
final int nparm = scope.getArgCount();
final Object[] copy = stack.clone();
int ncopy = 0;
if (values != null && values.length > 0) {
ncopy = Math.min(nparm - curried, Math.min(nparm, values.length));
System.arraycopy(values, 0, copy, curried, ncopy);
}
// unbound parameters are defined as null
Arrays.fill(copy, curried + ncopy, nparm, null);
return new Frame(scope, copy, curried + ncopy);
}
return this;
}
}

View File

@ -0,0 +1,318 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A range of integers.
*/
public abstract class IntegerRange implements Collection<Integer> {
/** The lower boundary. */
protected final int min;
/** The upper boundary. */
protected final int max;
/**
* Creates a range, ascending or descending depending on boundaries order.
*
* @param from the lower inclusive boundary
* @param to the higher inclusive boundary
* @return a range
*/
public static IntegerRange create(final int from, final int to) {
if (from <= to) {
return new Ascending(from, to);
}
return new Descending(to, from);
}
/**
* Creates a new range.
*
* @param from the lower inclusive boundary
* @param to the higher inclusive boundary
*/
public IntegerRange(final int from, final int to) {
min = from;
max = to;
}
/**
* Gets the interval minimum value.
*
* @return the low boundary
*/
public int getMin() {
return min;
}
/**
* Gets the interval maximum value.
*
* @return the high boundary
*/
public int getMax() {
return max;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
// CSOFF: MagicNumber
hash = 13 * hash + this.min;
hash = 13 * hash + this.max;
// CSON: MagicNumber
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final IntegerRange other = (IntegerRange) obj;
if (this.min != other.min) {
return false;
}
return this.max == other.max;
}
@Override
public abstract Iterator<Integer> iterator();
@Override
public int size() {
return max - min + 1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(final Object o) {
if (o instanceof Number) {
final long v = ((Number) o).intValue();
return min <= v && v <= max;
}
return false;
}
@Override
public Object[] toArray() {
final int size = size();
final Object[] array = new Object[size];
for (int a = 0; a < size; ++a) {
array[a] = min + a;
}
return array;
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(final T[] array) {
final Class<?> ct = array.getClass().getComponentType();
final int length = size();
T[] copy = array;
if (ct.isAssignableFrom(Integer.class)) {
if (array.length < length) {
copy = (T[]) Array.newInstance(ct, length);
}
for (int a = 0; a < length; ++a) {
Array.set(copy, a, min + a);
}
if (length < copy.length) {
copy[length] = null;
}
return copy;
}
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(final Collection<?> c) {
for (final Object cc : c) {
if (!contains(cc)) {
return false;
}
}
return true;
}
@Override
public boolean add(final Integer e) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(final Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(final Collection<? extends Integer> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
/**
* Ascending integer range.
*/
public static class Ascending extends IntegerRange {
/**
* Constructor.
*
* @param from lower boundary
* @param to upper boundary
*/
protected Ascending(final int from, final int to) {
super(from, to);
}
@Override
public Iterator<Integer> iterator() {
return new AscIntegerIterator(min, max);
}
}
/**
* Descending integer range.
*/
public static class Descending extends IntegerRange {
/**
* Constructor.
*
* @param from upper boundary
* @param to lower boundary
*/
protected Descending(final int from, final int to) {
super(from, to);
}
@Override
public Iterator<Integer> iterator() {
return new DescIntegerIterator(min, max);
}
}
}
/**
* An ascending iterator on an integer range.
*/
class AscIntegerIterator implements Iterator<Integer> {
/** The lower boundary. */
private final int min;
/** The upper boundary. */
private final int max;
/** The current value. */
private int cursor;
/**
* Creates a iterator on the range.
*
* @param l low boundary
* @param h high boundary
*/
public AscIntegerIterator(final int l, final int h) {
min = l;
max = h;
cursor = min;
}
@Override
public boolean hasNext() {
return cursor <= max;
}
@Override
public Integer next() {
if (cursor <= max) {
return cursor++;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}
/**
* A descending iterator on an integer range.
*/
class DescIntegerIterator implements Iterator<Integer> {
/** The lower boundary. */
private final int min;
/** The upper boundary. */
private final int max;
/** The current value. */
private int cursor;
/**
* Creates a iterator on the range.
*
* @param l low boundary
* @param h high boundary
*/
public DescIntegerIterator(final int l, final int h) {
min = l;
max = h;
cursor = max;
}
@Override
public boolean hasNext() {
return cursor >= min;
}
@Override
public Integer next() {
if (cursor >= min) {
return cursor--;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* The set of valued symbols defined in a lexical frame.
* <p>The symbol identifiers are determined by the functional scope.
*/
public class LexicalFrame extends LexicalScope {
/**
* The script frame.
*/
private final Frame frame;
/**
* Previous frame.
*/
protected final LexicalFrame previous;
/**
* The stack of values in the lexical frame.
*/
private Deque<Object> stack = null;
/**
* Lexical frame ctor.
*
* @param scriptf the script frame
* @param outerf the previous lexical frame
*/
public LexicalFrame(final Frame scriptf, final LexicalFrame outerf) {
this.previous = outerf;
this.frame = scriptf;
}
/**
* Copy ctor.
*
* @param src the frame to copy
*/
public LexicalFrame(final LexicalFrame src) {
super(src.symbols, src.moreSymbols);
frame = src.frame;
previous = src.previous;
stack = src.stack != null ? new ArrayDeque<>(src.stack) : null;
}
/**
* Define the arguments.
*
* @return this frame
*/
public LexicalFrame defineArgs() {
if (frame != null) {
final int argc = frame.getScope().getArgCount();
for (int a = 0; a < argc; ++a) {
super.addSymbol(a);
}
}
return this;
}
/**
* Defines a symbol.
*
* @param symbol the symbol to define
* @param capture whether this redefines a captured symbol
* @return true if symbol is defined, false otherwise
*/
public boolean defineSymbol(final int symbol, final boolean capture) {
final boolean declared = addSymbol(symbol);
if (declared && capture) {
if (stack == null) {
stack = new ArrayDeque<>();
}
stack.push(symbol);
Object value = frame.get(symbol);
if (value == null) {
value = this;
}
stack.push(value);
}
return declared;
}
/**
* Pops back values and lexical frame.
*
* @return the previous frame
*/
public LexicalFrame pop() {
// undefine all symbols
clearSymbols(s -> frame.set(s, Scope.UNDEFINED) );
// restore values of captured symbols that were overwritten
if (stack != null) {
while (!stack.isEmpty()) {
Object value = stack.pop();
if (value == Scope.UNDECLARED) {
value = Scope.UNDEFINED;
} else if (value == this) {
value = null;
}
final int symbol = (Integer) stack.pop();
frame.set(symbol, value);
}
}
return previous;
}
}

View File

@ -0,0 +1,136 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.util.BitSet;
/**
* The set of symbols declared in a lexical scope.
* <p>The symbol identifiers are determined by the functional scope.
*/
public class LexicalScope {
/**
* Number of bits in a long.
*/
protected static final int LONGBITS = 64;
/**
* The mask of symbols in the frame.
*/
protected long symbols = 0L;
/**
* Symbols after 64.
*/
protected BitSet moreSymbols = null;
/**
* Create a scope.
*/
public LexicalScope() {
}
/**
* Frame copy ctor base.
*
* @param s the symbols mask
* @param ms the more symbols bitset
*/
protected LexicalScope(final long s, final BitSet ms) {
symbols = s;
moreSymbols = ms != null ? (BitSet) ms.clone() : null;
}
/**
* Ensure more symbpls can be stored.
*
* @return the set of more symbols
*/
protected final BitSet moreSymbols() {
if (moreSymbols == null) {
moreSymbols = new BitSet();
}
return moreSymbols;
}
/**
* Checks whether a symbol has already been declared.
*
* @param symbol the symbol
* @return true if declared, false otherwise
*/
public boolean hasSymbol(final int symbol) {
if (symbol < LONGBITS) {
return (symbols & (1L << symbol)) != 0L;
}
return moreSymbols != null && moreSymbols.get(symbol - LONGBITS);
}
/**
* Adds a symbol in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
*/
public boolean addSymbol(final int symbol) {
if (symbol < LONGBITS) {
if ((symbols & (1L << symbol)) != 0L) {
return false;
}
symbols |= (1L << symbol);
} else {
final int s = symbol - LONGBITS;
final BitSet ms = moreSymbols();
if (ms.get(s)) {
return false;
}
ms.set(s, true);
}
return true;
}
/**
* Clear all symbols.
*
* @param cleanSymbol a (optional, may be null) functor to call for each cleaned symbol
*/
public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol) {
// undefine symbols getting out of scope
if (cleanSymbol != null) {
long clean = symbols;
while (clean != 0L) {
final int s = Long.numberOfTrailingZeros(clean);
clean &= ~(1L << s);
cleanSymbol.accept(s);
}
}
symbols = 0L;
if (moreSymbols != null) {
if (cleanSymbol != null) {
for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + 1)) {
cleanSymbol.accept(s + LONGBITS);
}
}
moreSymbols.clear();
}
}
/**
* @return the number of symbols defined in this scope.
*/
public int getSymbolCount() {
return Long.bitCount(symbols) + (moreSymbols == null ? 0 : moreSymbols.cardinality());
}
}

View File

@ -0,0 +1,319 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A range of longs.
* <p>
* Behaves as a readonly collection of longs.
*/
public abstract class LongRange implements Collection<Long> {
/** The lower boundary. */
protected final long min;
/** The upper boundary. */
protected final long max;
/**
* Creates a range, ascending or descending depending on boundaries order.
*
* @param from the lower inclusive boundary
* @param to the higher inclusive boundary
* @return a range
*/
public static LongRange create(final long from, final long to) {
if (from <= to) {
return new Ascending(from, to);
}
return new Descending(to, from);
}
/**
* Creates a new range.
*
* @param from the lower inclusive boundary
* @param to the higher inclusive boundary
*/
protected LongRange(final long from, final long to) {
min = from;
max = to;
}
/**
* Gets the interval minimum value.
*
* @return the low boundary
*/
public long getMin() {
return min;
}
/**
* Gets the interval maximum value.
*
* @return the high boundary
*/
public long getMax() {
return max;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
// CSOFF: MagicNumber
hash = 13 * hash + (int) (this.min ^ (this.min >>> 32));
hash = 13 * hash + (int) (this.max ^ (this.max >>> 32));
// CSON: MagicNumber
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final LongRange other = (LongRange) obj;
if (this.min != other.min) {
return false;
}
return this.max == other.max;
}
@Override
public abstract Iterator<Long> iterator();
@Override
public int size() {
return (int) (max - min + 1);
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(final Object o) {
if (o instanceof Number) {
final long v = ((Number) o).longValue();
return min <= v && v <= max;
}
return false;
}
@Override
public Object[] toArray() {
final int size = size();
final Object[] array = new Object[size];
for (int a = 0; a < size; ++a) {
array[a] = min + a;
}
return array;
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(final T[] array) {
final Class<?> ct = array.getClass().getComponentType();
final int length = size();
T[] copy = array;
if (ct.isAssignableFrom(Long.class)) {
if (array.length < length) {
copy = (T[]) Array.newInstance(ct, length);
}
for (int a = 0; a < length; ++a) {
Array.set(copy, a, min + a);
}
if (length < copy.length) {
copy[length] = null;
}
return copy;
}
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(final Collection<?> c) {
for (final Object cc : c) {
if (!contains(cc)) {
return false;
}
}
return true;
}
@Override
public boolean add(final Long e) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(final Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(final Collection<? extends Long> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
/**
* Ascending long range.
*/
public static class Ascending extends LongRange {
/**
* Constructor.
*
* @param from lower boundary
* @param to upper boundary
*/
protected Ascending(final long from, final long to) {
super(from, to);
}
@Override
public Iterator<Long> iterator() {
return new AscLongIterator(min, max);
}
}
/**
* Descending long range.
*/
public static class Descending extends LongRange {
/**
* Constructor.
*
* @param from upper boundary
* @param to lower boundary
*/
protected Descending(final long from, final long to) {
super(from, to);
}
@Override
public Iterator<Long> iterator() {
return new DescLongIterator(min, max);
}
}
}
/**
* An iterator on a long range.
*/
class AscLongIterator implements Iterator<Long> {
/** The lower boundary. */
private final long min;
/** The upper boundary. */
private final long max;
/** The current value. */
private long cursor;
/**
* Creates a iterator on the range.
*
* @param l low boundary
* @param h high boundary
*/
AscLongIterator(final long l, final long h) {
min = l;
max = h;
cursor = min;
}
@Override
public boolean hasNext() {
return cursor <= max;
}
@Override
public Long next() {
if (cursor <= max) {
return cursor++;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* An iterator on a long range.
*/
class DescLongIterator implements Iterator<Long> {
/** The lower boundary. */
private final long min;
/** The upper boundary. */
private final long max;
/** The current value. */
private long cursor;
/**
* Creates a iterator on the range.
*
* @param l low boundary
* @param h high boundary
*/
DescLongIterator(final long l, final long h) {
min = l;
max = h;
cursor = max;
}
@Override
public boolean hasNext() {
return cursor >= min;
}
@Override
public Long next() {
if (cursor >= min) {
return cursor--;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,48 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.util.HashMap;
import java.util.Map;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
/**
* Helper class to create map literals.
*/
public class MapBuilder implements JexlArithmetic.MapBuilder {
/** The map being created. */
protected final Map<Object, Object> map;
/**
* Creates a new builder.
* @param size the expected map size
*/
public MapBuilder(final int size) {
map = new HashMap<Object, Object>(size);
}
@Override
public void put(final Object key, final Object value) {
map.put(key, value);
}
@Override
public Map<Object,Object> create() {
return map;
}
}

View File

@ -0,0 +1,410 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.lang.reflect.Method;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection.MethodExecutor;
/**
* Helper class to deal with operator overloading and specifics.
* @since 3.0
*/
public class Operators {
/** The owner. */
protected final InterpreterBase interpreter;
/** The overloaded arithmetic operators. */
protected final JexlArithmetic.Uberspect operators;
/**
* Constructor.
* @param owner the owning interpreter
*/
protected Operators(final InterpreterBase owner) {
final JexlArithmetic arithmetic = owner.arithmetic;
final JexlUberspect uberspect = owner.uberspect;
this.interpreter = owner;
this.operators = uberspect.getArithmetic(arithmetic);
}
/**
* Checks whether a method returns a boolean or a Boolean.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean returnsBoolean(final JexlMethod vm) {
if (vm !=null) {
final Class<?> rc = vm.getReturnType();
return Boolean.TYPE.equals(rc) || Boolean.class.equals(rc);
}
return false;
}
/**
* Checks whether a method returns an int or an Integer.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean returnsInteger(final JexlMethod vm) {
if (vm !=null) {
final Class<?> rc = vm.getReturnType();
return Integer.TYPE.equals(rc) || Integer.class.equals(rc);
}
return false;
}
/**
* Checks whether a method is a JexlArithmetic method.
* @param vm the JexlMethod (may be null)
* @return true of false
*/
private boolean isArithmetic(final JexlMethod vm) {
if (vm instanceof MethodExecutor) {
final Method method = ((MethodExecutor) vm).getMethod();
return JexlArithmetic.class.equals(method.getDeclaringClass());
}
return false;
}
/**
* Attempts to call an operator.
* <p>
* This takes care of finding and caching the operator method when appropriate
* @param node the syntactic node
* @param operator the operator
* @param args the arguments
* @return the result of the operator evaluation or TRY_FAILED
*/
protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
if (operators != null && operators.overloads(operator)) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final boolean cache = interpreter.cache;
try {
if (cache) {
final Object cached = node.jjtGetValue();
if (cached instanceof JexlMethod) {
final JexlMethod me = (JexlMethod) cached;
final Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, args);
if (!me.tryFailed(eval)) {
return eval;
}
}
}
final JexlMethod vm = operators.getOperator(operator, args);
if (vm != null && !isArithmetic(vm)) {
final Object result = vm.invoke(arithmetic, args);
if (cache) {
node.jjtSetValue(vm);
}
return result;
}
} catch (final Exception xany) {
return interpreter.operatorError(node, operator, xany);
}
}
return JexlEngine.TRY_FAILED;
}
/**
* Evaluates an assign operator.
* <p>
* This takes care of finding and caching the operator method when appropriate.
* If an overloads returns Operator.ASSIGN, it means the side-effect is complete.
* Otherwise, a += b &lt;=&gt; a = a + b
* </p>
* @param node the syntactic node
* @param operator the operator
* @param args the arguments, the first one being the target of assignment
* @return JexlOperator.ASSIGN if operation assignment has been performed,
* JexlEngine.TRY_FAILED if no operation was performed,
* the value to use as the side effect argument otherwise
*/
protected Object tryAssignOverload(final JexlNode node, final JexlOperator operator, final Object...args) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
if (args.length != operator.getArity()) {
return JexlEngine.TRY_FAILED;
}
// try to call overload with side effect
Object result = tryOverload(node, operator, args);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
// call base operator
final JexlOperator base = operator.getBaseOperator();
if (base == null) {
throw new IllegalArgumentException("must be called with a side-effect operator");
}
if (operators != null && operators.overloads(base)) {
// in case there is an overload on the base operator
try {
final JexlMethod vm = operators.getOperator(base, args);
if (vm != null) {
result = vm.invoke(arithmetic, args);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
}
} catch (final Exception xany) {
interpreter.operatorError(node, base, xany);
}
}
// base eval
try {
switch (operator) {
case SELF_ADD:
return arithmetic.add(args[0], args[1]);
case SELF_SUBTRACT:
return arithmetic.subtract(args[0], args[1]);
case SELF_MULTIPLY:
return arithmetic.multiply(args[0], args[1]);
case SELF_DIVIDE:
return arithmetic.divide(args[0], args[1]);
case SELF_MOD:
return arithmetic.mod(args[0], args[1]);
case SELF_AND:
return arithmetic.and(args[0], args[1]);
case SELF_OR:
return arithmetic.or(args[0], args[1]);
case SELF_XOR:
return arithmetic.xor(args[0], args[1]);
default:
// unexpected, new operator added?
throw new UnsupportedOperationException(operator.getOperatorSymbol());
}
} catch (final Exception xany) {
interpreter.operatorError(node, base, xany);
}
return JexlEngine.TRY_FAILED;
}
/**
* The 'startsWith' operator implementation.
* @param node the node
* @param operator the calling operator, $= or $!
* @param left the left operand
* @param right the right operand
* @return true if left starts with right, false otherwise
*/
protected boolean startsWith(final JexlNode node, final String operator, final Object left, final Object right) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final JexlUberspect uberspect = interpreter.uberspect;
try {
// try operator overload
final Object result = tryOverload(node, JexlOperator.STARTSWITH, left, right);
if (result instanceof Boolean) {
return (Boolean) result;
}
// use arithmetic / pattern matching ?
final Boolean matched = arithmetic.startsWith(left, right);
if (matched != null) {
return matched;
}
// try a startsWith method (duck type)
try {
final Object[] argv = {right};
JexlMethod vm = uberspect.getMethod(left, "startsWith", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
if (arithmetic.narrowArguments(argv)) {
vm = uberspect.getMethod(left, "startsWith", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
}
} catch (final Exception e) {
throw new JexlException(node, operator + " error", e);
}
// defaults to equal
return arithmetic.equals(left, right);
} catch (final ArithmeticException xrt) {
throw new JexlException(node, operator + " error", xrt);
}
}
/**
* The 'endsWith' operator implementation.
* @param node the node
* @param operator the calling operator, ^= or ^!
* @param left the left operand
* @param right the right operand
* @return true if left ends with right, false otherwise
*/
protected boolean endsWith(final JexlNode node, final String operator, final Object left, final Object right) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final JexlUberspect uberspect = interpreter.uberspect;
try {
// try operator overload
final Object result = tryOverload(node, JexlOperator.ENDSWITH, left, right);
if (result instanceof Boolean) {
return (Boolean) result;
}
// use arithmetic / pattern matching ?
final Boolean matched = arithmetic.endsWith(left, right);
if (matched != null) {
return matched;
}
// try a endsWith method (duck type)
try {
final Object[] argv = {right};
JexlMethod vm = uberspect.getMethod(left, "endsWith", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
if (arithmetic.narrowArguments(argv)) {
vm = uberspect.getMethod(left, "endsWith", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
}
} catch (final Exception e) {
throw new JexlException(node, operator + " error", e);
}
// defaults to equal
return arithmetic.equals(left, right);
} catch (final ArithmeticException xrt) {
throw new JexlException(node, operator + " error", xrt);
}
}
/**
* The 'match'/'in' operator implementation.
* <p>
* Note that 'x in y' or 'x matches y' means 'y contains x' ;
* the JEXL operator arguments order syntax is the reverse of this method call.
* </p>
* @param node the node
* @param op the calling operator, =~ or !~
* @param right the left operand
* @param left the right operand
* @return true if left matches right, false otherwise
*/
protected boolean contains(final JexlNode node, final String op, final Object left, final Object right) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final JexlUberspect uberspect = interpreter.uberspect;
try {
// try operator overload
final Object result = tryOverload(node, JexlOperator.CONTAINS, left, right);
if (result instanceof Boolean) {
return (Boolean) result;
}
// use arithmetic / pattern matching ?
final Boolean matched = arithmetic.contains(left, right);
if (matched != null) {
return matched;
}
// try a contains method (duck type set)
try {
final Object[] argv = {right};
JexlMethod vm = uberspect.getMethod(left, "contains", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
if (arithmetic.narrowArguments(argv)) {
vm = uberspect.getMethod(left, "contains", argv);
if (returnsBoolean(vm)) {
return (Boolean) vm.invoke(left, argv);
}
}
} catch (final Exception e) {
throw new JexlException(node, op + " error", e);
}
// defaults to equal
return arithmetic.equals(left, right);
} catch (final ArithmeticException xrt) {
throw new JexlException(node, op + " error", xrt);
}
}
/**
* Check for emptyness of various types: Collection, Array, Map, String, and anything that has a boolean isEmpty()
* method.
* <p>Note that the result may not be a boolean.
*
* @param node the node holding the object
* @param object the object to check the emptyness of
* @return the evaluation result
*/
protected Object empty(final JexlNode node, final Object object) {
if (object == null) {
return true;
}
Object result = tryOverload(node, JexlOperator.EMPTY, object);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
final JexlArithmetic arithmetic = interpreter.arithmetic;
result = arithmetic.isEmpty(object, null);
if (result == null) {
final JexlUberspect uberspect = interpreter.uberspect;
result = false;
// check if there is an isEmpty method on the object that returns a
// boolean and if so, just use it
final JexlMethod vm = uberspect.getMethod(object, "isEmpty", Interpreter.EMPTY_PARAMS);
if (returnsBoolean(vm)) {
try {
result = vm.invoke(object, Interpreter.EMPTY_PARAMS);
} catch (final Exception xany) {
interpreter.operatorError(node, JexlOperator.EMPTY, xany);
}
}
}
return !(result instanceof Boolean) || (Boolean) result;
}
/**
* Calculate the <code>size</code> of various types:
* Collection, Array, Map, String, and anything that has a int size() method.
* <p>Note that the result may not be an integer.
*
* @param node the node that gave the value to size
* @param object the object to get the size of
* @return the evaluation result
*/
protected Object size(final JexlNode node, final Object object) {
if (object == null) {
return 0;
}
Object result = tryOverload(node, JexlOperator.SIZE, object);
if (result != JexlEngine.TRY_FAILED) {
return result;
}
final JexlArithmetic arithmetic = interpreter.arithmetic;
result = arithmetic.size(object, null);
if (result == null) {
final JexlUberspect uberspect = interpreter.uberspect;
// check if there is a size method on the object that returns an
// integer and if so, just use it
final JexlMethod vm = uberspect.getMethod(object, "size", Interpreter.EMPTY_PARAMS);
if (returnsInteger(vm)) {
try {
result = vm.invoke(object, Interpreter.EMPTY_PARAMS);
} catch (final Exception xany) {
interpreter.operatorError(node, JexlOperator.SIZE, xany);
}
}
}
return result instanceof Number ? ((Number) result).intValue() : 0;
}
}

View File

@ -0,0 +1,318 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A script scope, stores the declaration of parameters and local variables as symbols.
* <p>This also acts as the functional scope and variable definition store.
* @since 3.0
*/
public final class Scope {
/**
* The value of an as-yet undeclared but variable, for instance: x; before var x;.
*/
static final Object UNDECLARED = new Object() {
@Override public String toString() {
return "??";
}
};
/**
* The value of a declared but undefined variable, for instance: var x;.
*/
static final Object UNDEFINED = new Object() {
@Override public String toString() {
return "?";
}
};
/**
* The parent scope.
*/
private final Scope parent;
/**
* The number of parameters.
*/
private int parms;
/**
* The number of local variables.
*/
private int vars;
/**
* The map of named variables aka script parameters and local variables.
* Each parameter is associated to a symbol and is materialized as an offset in the stacked array used
* during evaluation.
*/
private Map<String, Integer> namedVariables = null;
/**
* The map of local captured variables to parent scope variables, ie closure.
*/
private Map<Integer, Integer> capturedVariables = null;
/**
* The empty string array.
*/
private static final String[] EMPTY_STRS = new String[0];
/**
* Creates a new scope with a list of parameters.
* @param scope the parent scope if any
* @param parameters the list of parameters
*/
public Scope(final Scope scope, final String... parameters) {
if (parameters != null) {
parms = parameters.length;
namedVariables = new LinkedHashMap<String, Integer>();
for (int p = 0; p < parms; ++p) {
namedVariables.put(parameters[p], p);
}
} else {
parms = 0;
}
vars = 0;
parent = scope;
}
@Override
public int hashCode() {
return namedVariables == null ? 0 : parms ^ namedVariables.hashCode();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Scope)) {
return false;
}
final Scope scope = (Scope) o;
if (parms != scope.parms) {
return false;
}
if (namedVariables == null) {
return scope.namedVariables == null;
}
return namedVariables.equals(scope.namedVariables);
}
/**
* Checks whether an identifier is a local variable or argument, ie a symbol.
* If this fails, look in parents for symbol that can be captured.
* @param name the symbol name
* @return the symbol index
*/
public Integer getSymbol(final String name) {
return getSymbol(name, true);
}
/**
* Checks whether an identifier is a local variable or argument, ie a symbol.
* @param name the symbol name
* @param capture whether solving by capturing a parent symbol is allowed
* @return the symbol index
*/
private Integer getSymbol(final String name, final boolean capture) {
Integer register = namedVariables != null ? namedVariables.get(name) : null;
if (register == null && capture && parent != null) {
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
capturedVariables = new LinkedHashMap<Integer, Integer>();
}
if (namedVariables == null) {
namedVariables = new LinkedHashMap<String, Integer>();
}
register = namedVariables.size();
namedVariables.put(name, register);
capturedVariables.put(register, pr);
}
}
return register;
}
/**
* Checks whether a given symbol is captured.
* @param symbol the symbol number
* @return true if captured, false otherwise
*/
public boolean isCapturedSymbol(final int symbol) {
return capturedVariables != null && capturedVariables.containsKey(symbol);
}
/**
* Declares a parameter.
* <p>
* This method creates an new entry in the symbol map.
* </p>
* @param name the parameter name
* @return the register index storing this variable
*/
public int declareParameter(final String name) {
if (namedVariables == null) {
namedVariables = new LinkedHashMap<String, Integer>();
} else if (vars > 0) {
throw new IllegalStateException("cant declare parameters after variables");
}
Integer register = namedVariables.get(name);
if (register == null) {
register = namedVariables.size();
namedVariables.put(name, register);
parms += 1;
}
return register;
}
/**
* Declares a local variable.
* <p>
* This method creates an new entry in the symbol map.
* </p>
* @param name the variable name
* @return the register index storing this variable
*/
public int declareVariable(final String name) {
if (namedVariables == null) {
namedVariables = new LinkedHashMap<String, Integer>();
}
Integer register = namedVariables.get(name);
if (register == null) {
register = namedVariables.size();
namedVariables.put(name, register);
vars += 1;
// check if local is redefining captured
if (parent != null) {
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
capturedVariables = new LinkedHashMap<Integer, Integer>();
}
capturedVariables.put(register, pr);
}
}
}
return register;
}
/**
* Creates a frame by copying values up to the number of parameters.
* <p>This captures the captured variables values.</p>
* @param frame the caller frame
* @param args the arguments
* @return the arguments array
*/
public Frame createFrame(final Frame frame, final Object...args) {
if (namedVariables == null) {
return null;
}
final Object[] arguments = new Object[namedVariables.size()];
Arrays.fill(arguments, UNDECLARED);
if (frame != null && capturedVariables != null && parent != null) {
for (final Map.Entry<Integer, Integer> capture : capturedVariables.entrySet()) {
final Integer target = capture.getKey();
final Integer source = capture.getValue();
final Object arg = frame.get(source);
arguments[target] = arg;
}
}
return new Frame(this, arguments, 0).assign(args);
}
/**
* Gets the captured index of a given symbol, ie the target index of a symbol in a child frame.
* @param symbol the symbol index
* @return the target symbol index or null if the symbol is not captured
*/
public Integer getCaptured(final int symbol) {
if (capturedVariables != null) {
for (final Map.Entry<Integer, Integer> capture : capturedVariables.entrySet()) {
final Integer source = capture.getValue();
if (source == symbol) {
return capture.getKey();
}
}
}
return null;
}
/**
* Gets the (maximum) number of arguments this script expects.
* @return the number of parameters
*/
public int getArgCount() {
return parms;
}
/**
* Gets this script symbols names, i.e. parameters and local variables.
* @return the symbol names
*/
public String[] getSymbols() {
return namedVariables != null ? namedVariables.keySet().toArray(new String[0]) : EMPTY_STRS;
}
/**
* Gets this script parameters, i.e. symbols assigned before creating local variables.
* @return the parameter names
*/
public String[] getParameters() {
return getParameters(0);
}
/**
* Gets this script parameters.
* @param bound number of known bound parameters (curry)
* @return the parameter names
*/
protected String[] getParameters(final int bound) {
final int unbound = parms - bound;
if ((namedVariables == null) || (unbound <= 0)) {
return EMPTY_STRS;
}
final String[] pa = new String[unbound];
int p = 0;
for (final Map.Entry<String, Integer> entry : namedVariables.entrySet()) {
final int argn = entry.getValue();
if (argn >= bound && argn < parms) {
pa[p++] = entry.getKey();
}
}
return pa;
}
/**
* Gets this script local variable, i.e. symbols assigned to local variables excluding captured variables.
* @return the local variable names
*/
public String[] getLocalVariables() {
if ((namedVariables == null) || (vars <= 0)) {
return EMPTY_STRS;
}
final List<String> locals = new ArrayList<String>(vars);
for (final Map.Entry<String, Integer> entry : namedVariables.entrySet()) {
final int symnum = entry.getValue();
if (symnum >= parms && (capturedVariables == null || !capturedVariables.containsKey(symnum))) {
locals.add(entry.getKey());
}
}
return locals.toArray(new String[locals.size()]);
}
}

View File

@ -0,0 +1,342 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlExpression;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* <p>A JexlScript implementation.</p>
* @since 1.1
*/
public class Script implements JexlScript, JexlExpression {
/**
* The engine for this expression.
*/
protected final Engine jexl;
/**
* Original expression stripped from leading and trailing spaces.
*/
protected final String source;
/**
* The resulting AST we can interpret.
*/
protected final ASTJexlScript script;
/**
* The engine version (as class loader change count) that last evaluated this script.
*/
protected int version;
/**
* @return the script AST
*/
protected ASTJexlScript getScript() {
return script;
}
/**
* Do not let this be generally instantiated with a 'new'.
*
* @param engine the interpreter to evaluate the expression
* @param expr the expression source.
* @param ref the parsed expression.
*/
protected Script(final Engine engine, final String expr, final ASTJexlScript ref) {
jexl = engine;
source = expr;
script = ref;
version = jexl.getUberspect().getVersion();
}
/**
* Checks that this script cached methods (wrt introspection) matches the engine version.
* <p>
* If the engine class loader has changed since we last evaluated this script, the script local cache
* is invalidated to drop references to obsolete methods. It is not strictly necessary since the tryExecute
* will fail because the class won't match but it seems cleaner nevertheless.
* </p>
*/
protected void checkCacheVersion() {
final int uberVersion = jexl.getUberspect().getVersion();
if (version != uberVersion) {
// version 0 of the uberSpect is an illusion due to order of construction; no need to clear cache
if (version > 0) {
script.clearCache();
}
version = uberVersion;
}
}
/**
* Creates this script frame for evaluation.
* @param args the arguments to bind to parameters
* @return the frame (may be null)
*/
protected Frame createFrame(final Object[] args) {
return script.createFrame(args);
}
/**
* Creates this script interpreter.
* @param context the context
* @param frame the calling frame
* @return the interpreter
*/
protected Interpreter createInterpreter(final JexlContext context, final Frame frame) {
final JexlOptions opts = jexl.options(script, context);
return jexl.createInterpreter(context, frame, opts);
}
/**
* @return the engine that created this script
*/
public JexlEngine getEngine() {
return jexl;
}
@Override
public String getSourceText() {
return source;
}
@Override
public String getParsedText() {
return getParsedText(2);
}
@Override
public String getParsedText(final int indent) {
final Debugger debug = new Debugger();
debug.setIndentation(indent);
debug.debug(script, false);
return debug.toString();
}
@Override
public String toString() {
CharSequence src = source;
if (src == null) {
final Debugger debug = new Debugger();
debug.debug(script, false);
src = debug.toString();
}
return src.toString();
}
@Override
public int hashCode() {
// CSOFF: Magic number
int hash = 17;
hash = 31 * hash + (this.jexl != null ? this.jexl.hashCode() : 0);
hash = 31 * hash + (this.source != null ? this.source.hashCode() : 0);
return hash;
// CSON: Magic number
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Script other = (Script) obj;
if (this.jexl != other.jexl) {
return false;
}
if (!Objects.equals(this.source, other.source)) {
return false;
}
return true;
}
@Override
public Object evaluate(final JexlContext context) {
return execute(context);
}
@Override
public Object execute(final JexlContext context) {
checkCacheVersion();
final Frame frame = createFrame(null);
final Interpreter interpreter = createInterpreter(context, frame);
return interpreter.interpret(script);
}
@Override
public Object execute(final JexlContext context, final Object... args) {
checkCacheVersion();
final Frame frame = createFrame(args != null && args.length > 0 ? args : null);
final Interpreter interpreter = createInterpreter(context, frame);
return interpreter.interpret(script);
}
@Override
public JexlScript curry(final Object... args) {
final String[] parms = script.getParameters();
if (parms == null || parms.length == 0) {
return this;
}
return new Closure(this, args);
}
@Override
public String[] getParameters() {
return script.getParameters();
}
@Override
public String[] getUnboundParameters() {
return getParameters();
}
@Override
public String[] getLocalVariables() {
return script.getLocalVariables();
}
/**
* @return the info
*/
public JexlInfo getInfo() {
return script.jexlInfo();
}
/**
* @return the script features
*/
public JexlFeatures getFeatures() {
return script.getFeatures();
}
/**
* Gets this script variables.
* <p>Note that since variables can be in an ant-ish form (ie foo.bar.quux), each variable is returned as
* a list of strings where each entry is a fragment of the variable ({"foo", "bar", "quux"} in the example.</p>
* @return the variables or null
*/
@Override
public Set<List<String>> getVariables() {
return jexl.getVariables(script);
}
/**
* Get this script pragmas
* <p>Pragma keys are ant-ish variables, their values are scalar literals..
* @return the pragmas
*/
@Override
public Map<String, Object> getPragmas() {
return script.getPragmas();
}
/**
* Creates a Callable from this script.
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
* @param context the context
* @return the callable
*/
@Override
public Callable callable(final JexlContext context) {
return callable(context, (Object[]) null);
}
/**
* Creates a Callable from this script.
* <p>This allows to submit it to an executor pool and provides support for asynchronous calls.</p>
* <p>The interpreter will handle interruption/cancellation gracefully if needed.</p>
* @param context the context
* @param args the script arguments
* @return the callable
*/
@Override
public Callable callable(final JexlContext context, final Object... args) {
return new Callable(createInterpreter(context, script.createFrame(args)));
}
/**
* Implements the Future and Callable interfaces to help delegation.
*/
public class Callable implements java.util.concurrent.Callable<Object> {
/** The actual interpreter. */
protected final Interpreter interpreter;
/** Use interpreter as marker for not having run. */
protected volatile Object result;
/**
* The base constructor.
* @param intrprtr the interpreter to use
*/
protected Callable(final Interpreter intrprtr) {
this.interpreter = intrprtr;
this.result = intrprtr;
}
/**
* Run the interpreter.
* @return the evaluation result
*/
protected Object interpret() {
return interpreter.interpret(script);
}
@Override
public Object call() throws Exception {
synchronized(this) {
if (result == interpreter) {
checkCacheVersion();
result = interpret();
}
return result;
}
}
/**
* Soft cancel the execution.
* @return true if cancel was successful, false otherwise
*/
public boolean cancel() {
return interpreter.cancel();
}
/**
* @return true if evaluation was cancelled, false otherwise
*/
public boolean isCancelled() {
return interpreter.isCancelled();
}
/**
* @return true if interruption will throw a JexlException.Cancel, false otherwise
*/
public boolean isCancellable() {
return interpreter.isCancellable();
}
}
}

View File

@ -0,0 +1,502 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlExpression;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlScript;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAddNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAndNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAnnotation;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArguments;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayAccess;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArrayLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTAssignment;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBlock;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTBreak;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTConstructorNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTContinue;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDivNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTDoWhileStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEQNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTERNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEWNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTEmptyFunction;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTExtendedLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFalseNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTForeachStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGENode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTGTNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifierAccess;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIfStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJxltLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLENode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTLTNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapEntry;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMapLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMethodNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTModNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTMulNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNENode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNEWNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNRNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNSWNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNotNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNullpNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNumberLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTOrNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRangeNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReference;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReferenceExpression;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTRegexLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTReturnStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSWNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAddNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetAndNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetDivNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetModNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetMultNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetOrNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetSubNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSetXorNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSizeFunction;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTStringLiteral;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTSubNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTernaryNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTTrueNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTVar;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTWhileStatement;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ParserVisitor;
/**
* Fully abstract to avoid public interface exposition.
*/
public class ScriptVisitor extends ParserVisitor {
/**
* Visits all AST constituents of a JEXL expression.
* @param jscript the expression
* @param data some data context
* @return the visit result or null if jscript was not a Script implementation
*/
public Object visitExpression (final JexlExpression jscript, final Object data) {
if (jscript instanceof Script) {
return ((Script) jscript).getScript().jjtAccept(this, data);
}
return null;
}
/**
* Visits all AST constituents of a JEXL script.
* @param jscript the expression
* @param data some data context
* @return the visit result or null if jscript was not a Script implementation
*/
public Object visitScript(final JexlScript jscript, final Object data) {
if (jscript instanceof Script) {
return ((Script) jscript).getScript().jjtAccept(this, data);
}
return null;
}
/**
* Visits a node.
* Default implementation visits all its children.
* @param node the node to visit
* @param data visitor pattern argument
* @return visitor pattern value
*/
protected Object visitNode(final JexlNode node, final Object data) {
return node.childrenAccept(this, data);
}
@Override
protected Object visit(final ASTJexlScript node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBlock node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTIfStatement node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTWhileStatement node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTDoWhileStatement node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTContinue node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBreak node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTForeachStatement node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTReturnStatement node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTAssignment node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTVar node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTReference node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTTernaryNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNullpNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTOrNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTAndNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBitwiseOrNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBitwiseXorNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBitwiseAndNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTEQNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNENode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTLTNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTGTNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTLENode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTGENode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTERNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNRNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSWNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNSWNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTEWNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNEWNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTAddNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSubNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTMulNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTDivNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTModNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTUnaryMinusNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTUnaryPlusNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTBitwiseComplNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNotNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTIdentifier node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNullLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTTrueNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTFalseNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTNumberLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTStringLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTRegexLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTExtendedLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTArrayLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTRangeNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTMapLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTMapEntry node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTEmptyFunction node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSizeFunction node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTFunctionNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTMethodNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTConstructorNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTArrayAccess node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTIdentifierAccess node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTArguments node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTReferenceExpression node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetAddNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetSubNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetMultNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetDivNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetModNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetAndNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetOrNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTSetXorNode node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTJxltLiteral node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTAnnotation node, final Object data) {
return visitNode(node, data);
}
@Override
protected Object visit(final ASTAnnotatedStatement node, final Object data) {
return visitNode(node, data);
}
}

View File

@ -0,0 +1,48 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import java.util.HashSet;
import java.util.Set;
/**
* Helper class to create set literals.
*/
public class SetBuilder implements JexlArithmetic.SetBuilder {
/** The set being created. */
protected final Set<Object> set;
/**
* Creates a new builder.
* @param size the expected set size
*/
public SetBuilder(final int size) {
set = new HashSet<Object>(size);
}
@Override
public void add(final Object value) {
set.add(value);
}
@Override
public Object create() {
return set;
}
}

View File

@ -0,0 +1,211 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A soft referenced cache.
* <p>
* The actual cache is held through a soft reference, allowing it to be GCed
* under memory pressure.</p>
*
* @param <K> the cache key entry type
* @param <V> the cache key value type
*/
public class SoftCache<K, V> {
/**
* The default cache load factor.
*/
private static final float LOAD_FACTOR = 0.75f;
/**
* The cache size.
*/
private final int size;
/**
* The soft reference to the cache map.
*/
private SoftReference<Map<K, V>> ref = null;
/**
* The cache r/w lock.
*/
private final ReadWriteLock lock;
/**
* Creates a new instance of a soft cache.
*
* @param theSize the cache size
*/
SoftCache(final int theSize) {
size = theSize;
lock = new ReentrantReadWriteLock();
}
/**
* Returns the cache size.
*
* @return the cache size
*/
public int size() {
return size;
}
/**
* Clears the cache.
*/
public void clear() {
lock.writeLock().lock();
try {
ref = null;
} finally {
lock.writeLock().unlock();
}
}
/**
* Gets a value from cache.
*
* @param key the cache entry key
* @return the cache entry value
*/
public V get(final K key) {
lock.readLock().lock();
try {
final Map<K, V> map = ref != null ? ref.get() : null;
return map != null ? map.get(key) : null;
} finally {
lock.readLock().unlock();
}
}
/**
* Puts a value in cache.
*
* @param key the cache entry key
* @param script the cache entry value
*/
public void put(final K key, final V script) {
lock.writeLock().lock();
try {
Map<K, V> map = ref != null ? ref.get() : null;
if (map == null) {
map = createCache(size);
ref = new SoftReference<Map<K, V>>(map);
}
map.put(key, script);
} finally {
lock.writeLock().unlock();
}
}
/**
* Produces the cache entry set.
* <p>
* For testing only, perform deep copy of cache entries
*
* @return the cache entry list
*/
public List<Map.Entry<K, V>> entries() {
lock.readLock().lock();
try {
final Map<K, V> map = ref != null ? ref.get() : null;
if (map == null) {
return Collections.emptyList();
}
final Set<Map.Entry<K, V>> set = map.entrySet();
final List<Map.Entry<K, V>> entries = new ArrayList<Map.Entry<K, V>>(set.size());
for (final Map.Entry<K, V> e : set) {
entries.add(new SoftCacheEntry<K, V>(e));
}
return entries;
} finally {
lock.readLock().unlock();
}
}
/**
* Creates the cache store.
*
* @param <K> the key type
* @param <V> the value type
* @param cacheSize the cache size, must be &gt; 0
* @return a Map usable as a cache bounded to the given size
*/
public <K, V> Map<K, V> createCache(final int cacheSize) {
return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true) {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
return super.size() > cacheSize;
}
};
}
}
/**
* A soft cache entry.
*
* @param <K> key type
* @param <V> value type
*/
class SoftCacheEntry<K, V> implements Map.Entry<K, V> {
/**
* The entry key.
*/
private final K key;
/**
* The entry value.
*/
private final V value;
/**
* Creates an entry clone.
*
* @param e the entry to clone
*/
SoftCacheEntry(final Map.Entry<K, V> e) {
key = e.getKey();
value = e.getValue();
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(final V v) {
throw new UnsupportedOperationException("Not supported.");
}
}

View File

@ -0,0 +1,95 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlFeatures;
import java.util.Objects;
/**
* Maintains the set of allowed features associated with a script/expression source.
* <p>This is meant for caching scripts using their 'source' as key but still distinguishing
* scripts with different features and prevent false sharing.
*/
public final class Source {
/** The hash code, pre-computed for fast op. */
private final int hashCode;
/** The set of features. */
private final JexlFeatures features;
/** The actual source script/expression. */
private final String str;
/**
* Default constructor.
* @param theFeatures the features
* @param theStr the script source
*/
Source(final JexlFeatures theFeatures, final String theStr) { // CSOFF: MagicNumber
this.features = theFeatures;
this.str = theStr;
int hash = 3;
hash = 37 * hash + features.hashCode();
hash = 37 * hash + str.hashCode() ;
this.hashCode = hash;
}
/**
* @return the length of the script source
*/
int length() {
return str.length();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Source other = (Source) obj;
if (!Objects.equals(this.features, other.features)) {
return false;
}
if (!Objects.equals(this.str, other.str)) {
return false;
}
return true;
}
@Override
public String toString() {
return str;
}
/**
* @return the features associated with the source
*/
public JexlFeatures getFeatures() {
return features;
}
}

View File

@ -0,0 +1,314 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.TemplateEngine.*;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.*;
/**
* A visitor for templates.
* <p>A friend (ala C++) of template engine.
*/
public class TemplateDebugger extends Debugger {
/** The outer script. */
private ASTJexlScript script;
/** The expressions called by the script through jexl:print. */
private TemplateExpression[] exprs;
/**
* Default ctor.
*/
public TemplateDebugger() {
}
@Override
public void reset() {
super.reset();
// so we can use it more than one time
exprs = null;
script = null;
}
/**
* Position the debugger on the root of a template expression.
*
* @param je the expression
* @return true if the expression was a {@link TemplateExpression} instance, false otherwise
*/
public boolean debug(final JxltEngine.Expression je) {
if (je instanceof TemplateExpression) {
final TemplateExpression te = (TemplateExpression) je;
return visit(te, this) != null;
}
return false;
}
/**
* Position the debugger on the root of a template script.
*
* @param jt the template
* @return true if the template was a {@link TemplateScript} instance, false otherwise
*/
public boolean debug(final JxltEngine.Template jt) {
if (!(jt instanceof TemplateScript)) {
return false;
}
final TemplateScript ts = (TemplateScript) jt;
// ensure expr is not null for templates
this.exprs = ts.getExpressions() == null ? new TemplateExpression[0] : ts.getExpressions();
this.script = ts.getScript();
start = 0;
end = 0;
indentLevel = 0;
builder.setLength(0);
cause = script;
final int num = script.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
final JexlNode child = script.jjtGetChild(i);
acceptStatement(child, null);
}
// the last line
if (builder.length() > 0 && builder.charAt(builder.length() - 1) != '\n') {
builder.append('\n');
}
end = builder.length();
return end > 0;
}
@Override
protected Object visit(final ASTBlock node, final Object data) {
// if not really a template, must use super impl
if (exprs == null) {
return super.visit(node, data);
}
// open the block
builder.append('{');
if (indent > 0) {
indentLevel += 1;
builder.append('\n');
} else {
builder.append(' ');
}
final int num = node.jjtGetNumChildren();
for (int i = 0; i < num; ++i) {
final JexlNode child = node.jjtGetChild(i);
acceptStatement(child, data);
}
// before we close this block node, $$ might be needed
newJexlLine();
if (indent > 0) {
indentLevel -= 1;
for (int i = 0; i < indentLevel; ++i) {
for (int s = 0; s < indent; ++s) {
builder.append(' ');
}
}
}
builder.append('}');
// closed the block
return data;
}
@Override
protected Object acceptStatement(final JexlNode child, final Object data) {
// if not really a template, must use super impl
if (exprs == null) {
return super.acceptStatement(child, data);
}
final TemplateExpression te = getPrintStatement(child);
if (te != null) {
// if statement is a jexl:print(...), may need to prepend '\n'
newJxltLine();
return visit(te, data);
}
// if statement is not a jexl:print(...), need to prepend '$$'
newJexlLine();
return super.acceptStatement(child, data);
}
/**
* In a template, any statement that is not 'jexl:print(n)' must be prefixed by "$$".
*
* @param child the node to check
* @return the expression number or -1 if the node is not a jexl:print
*/
private TemplateExpression getPrintStatement(final JexlNode child) {
if (exprs != null && child instanceof ASTFunctionNode) {
final ASTFunctionNode node = (ASTFunctionNode) child;
final ASTIdentifier ns = (ASTIdentifier) node.jjtGetChild(0);
final JexlNode args = node.jjtGetChild(1);
if ("jexl".equals(ns.getNamespace())
&& "print".equals(ns.getName())
&& args.jjtGetNumChildren() == 1
&& args.jjtGetChild(0) instanceof ASTNumberLiteral) {
final ASTNumberLiteral exprn = (ASTNumberLiteral) args.jjtGetChild(0);
final int n = exprn.getLiteral().intValue();
if (n >= 0 && n < exprs.length) {
return exprs[n];
}
}
}
return null;
}
/**
* Insert $$ and \n when needed.
*/
private void newJexlLine() {
final int length = builder.length();
if (length == 0) {
builder.append("$$ ");
} else {
for (int i = length - 1; i >= 0; --i) {
final char c = builder.charAt(i);
switch (c) {
case '\n':
builder.append("$$ ");
return;
case '}':
builder.append("\n$$ ");
return;
case ' ':
case ';':
return;
default: // continue
}
}
}
}
/**
* Insert \n when needed.
*/
private void newJxltLine() {
final int length = builder.length();
for (int i = length - 1; i >= 0; --i) {
final char c = builder.charAt(i);
switch (c) {
case '\n':
case ';':
return;
case '}':
builder.append('\n');
return;
default: // continue
}
}
}
/**
* Visit a template expression.
*
* @param expr the constant expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final TemplateExpression expr, final Object data) {
Object r;
switch (expr.getType()) {
case CONSTANT:
r = visit((ConstantExpression) expr, data);
break;
case IMMEDIATE:
r = visit((ImmediateExpression) expr, data);
break;
case DEFERRED:
r = visit((DeferredExpression) expr, data);
break;
case NESTED:
r = visit((NestedExpression) expr, data);
break;
case COMPOSITE:
r = visit((CompositeExpression) expr, data);
break;
default:
r = null;
}
return r;
}
/**
* Visit a constant expression.
*
* @param expr the constant expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final ConstantExpression expr, final Object data) {
expr.asString(builder);
return data;
}
/**
* Visit an immediate expression.
*
* @param expr the immediate expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final ImmediateExpression expr, final Object data) {
builder.append(expr.isImmediate() ? '$' : '#');
builder.append('{');
super.accept(expr.node, data);
builder.append('}');
return data;
}
/**
* Visit a deferred expression.
*
* @param expr the deferred expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final DeferredExpression expr, final Object data) {
builder.append(expr.isImmediate() ? '$' : '#');
builder.append('{');
super.accept(expr.node, data);
builder.append('}');
return data;
}
/**
* Visit a nested expression.
*
* @param expr the nested expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final NestedExpression expr, final Object data) {
super.accept(expr.node, data);
return data;
}
/**
* Visit a composite expression.
*
* @param expr the composite expression
* @param data the visitor argument
* @return the visitor argument
*/
private Object visit(final CompositeExpression expr, final Object data) {
for (final TemplateExpression ce : expr.exprs) {
visit(ce, data);
}
return data;
}
}

View File

@ -0,0 +1,305 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions;
import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.*;
import java.io.Writer;
import java.util.Arrays;
/**
* The type of interpreter to use during evaluation of templates.
* <p>This context exposes its writer as '$jexl' to the scripts.</p>
* <p>public for introspection purpose.</p>
*/
public class TemplateInterpreter extends Interpreter {
/** The array of template expressions. */
private final TemplateEngine.TemplateExpression[] exprs;
/** The writer used to output. */
private final Writer writer;
/**
* Helper ctor.
* <p>Stores the different properties required to create a Template interpreter.
*/
static class Arguments {
/** The engine. */
Engine jexl;
/** The options. */
JexlOptions options;
/** The context. */
JexlContext jcontext;
/** The frame. */
Frame jframe;
/** The expressions. */
TemplateEngine.TemplateExpression[] expressions;
/** The writer. */
Writer out;
/**
* Sole ctor.
*
* @param e the JEXL engine
*/
Arguments(final Engine e) {
this.jexl = e;
}
/**
* Sets the options.
*
* @param o the options
* @return this instance
*/
Arguments options(final JexlOptions o) {
this.options = o;
return this;
}
/**
* Sets the context.
*
* @param j the context
* @return this instance
*/
Arguments context(final JexlContext j) {
this.jcontext = j;
return this;
}
/**
* Sets the frame.
*
* @param f the frame
* @return this instance
*/
Arguments frame(final Frame f) {
this.jframe = f;
return this;
}
/**
* Sets the expressions.
*
* @param e the expressions
* @return this instance
*/
Arguments expressions(final TemplateEngine.TemplateExpression[] e) {
this.expressions = e;
return this;
}
/**
* Sets the writer.
*
* @param o the writer
* @return this instance
*/
Arguments writer(final Writer o) {
this.out = o;
return this;
}
}
/**
* Creates a template interpreter instance.
*
* @param args the template interpreter arguments
*/
TemplateInterpreter(final Arguments args) {
super(args.jexl, args.options, args.jcontext, args.jframe);
exprs = args.expressions;
writer = args.out;
block = new LexicalFrame(frame, null);
}
/**
* Includes a call to another template.
* <p>
* Includes another template using this template initial context and writer.</p>
*
* @param script the TemplateScript to evaluate
* @param args the arguments
*/
public void include(final JxltEngine.Template script, final Object... args) {
script.evaluate(context, writer, args);
}
/**
* Prints a unified expression evaluation result.
*
* @param e the expression number
*/
public void print(final int e) {
if (e < 0 || e >= exprs.length) {
return;
}
TemplateEngine.TemplateExpression expr = exprs[e];
if (expr.isDeferred()) {
expr = expr.prepare(frame, context);
}
if (expr instanceof TemplateEngine.CompositeExpression) {
printComposite((TemplateEngine.CompositeExpression) expr);
} else {
doPrint(expr.getInfo(), expr.evaluate(this));
}
}
/**
* Prints a composite expression.
*
* @param composite the composite expression
*/
private void printComposite(final TemplateEngine.CompositeExpression composite) {
final TemplateEngine.TemplateExpression[] cexprs = composite.exprs;
Object value;
for (final TemplateEngine.TemplateExpression cexpr : cexprs) {
value = cexpr.evaluate(this);
doPrint(cexpr.getInfo(), value);
}
}
/**
* Prints to output.
* <p>
* This will dynamically try to find the best suitable method in the writer through uberspection.
* Subclassing Writer by adding 'print' methods should be the preferred way to specialize output.
* </p>
*
* @param info the source info
* @param arg the argument to print out
*/
private void doPrint(final JexlInfo info, final Object arg) {
try {
if (writer != null) {
if (arg instanceof CharSequence) {
writer.write(arg.toString());
} else if (arg != null) {
final Object[] value = {arg};
final JexlUberspect uber = jexl.getUberspect();
final JexlMethod method = uber.getMethod(writer, "print", value);
if (method != null) {
method.invoke(writer, value);
} else {
writer.write(arg.toString());
}
}
}
} catch (final java.io.IOException xio) {
throw TemplateEngine.createException(info, "call print", null, xio);
} catch (final Exception xany) {
throw TemplateEngine.createException(info, "invoke print", null, xany);
}
}
@Override
protected Object resolveNamespace(final String prefix, final JexlNode node) {
return "jexl".equals(prefix) ? this : super.resolveNamespace(prefix, node);
}
@Override
protected Object visit(final ASTIdentifier node, final Object data) {
final String name = node.getName();
if ("$jexl".equals(name)) {
return writer;
}
return super.visit(node, data);
}
/**
* Interprets a function node.
* print() and include() must be decoded by this interpreter since delegating to the Uberspect
* may be sandboxing the interpreter itself making it unable to call the function.
*
* @param node the function node
* @param data the data
* @return the function evaluation result.
*/
@Override
protected Object visit(final ASTFunctionNode node, Object data) {
final int argc = node.jjtGetNumChildren();
if (argc == 2) {
final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0);
if ("jexl".equals(functionNode.getNamespace())) {
final String functionName = functionNode.getName();
final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
if ("print".equals(functionName)) {
// evaluate the arguments
Object[] argv = visit(argNode, null);
if (argv != null && argv.length > 0 && argv[0] instanceof Number) {
print(((Number) argv[0]).intValue());
return null;
}
}
if ("include".equals(functionName)) {
// evaluate the arguments
Object[] argv = visit(argNode, null);
if (argv != null && argv.length > 0) {
if (argv[0] instanceof TemplateScript) {
TemplateScript script = (TemplateScript) argv[0];
if (argv.length > 1) {
argv = Arrays.copyOfRange(argv, 1, argv.length);
} else {
argv = null;
}
include(script, argv);
return null;
}
}
}
// fail safe
throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + functionName, null);
}
}
return super.visit(node, data);
}
@Override
protected Object visit(final ASTJexlScript script, final Object data) {
if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) {
return new Closure(this, (ASTJexlLambda) script) {
@Override
protected Interpreter createInterpreter(final JexlContext context, final Frame local) {
final JexlOptions opts = jexl.options(script, context);
final Arguments targs = new Arguments(jexl)
.context(context)
.options(opts)
.frame(local)
.expressions(exprs)
.writer(writer);
return new TemplateInterpreter(targs);
}
};
}
// otherwise...
final int numChildren = script.jjtGetNumChildren();
Object result = null;
for (int i = 0; i < numChildren; i++) {
final JexlNode child = script.jjtGetChild(i);
result = child.jjtAccept(this, data);
cancelCheck(child);
}
return result;
}
}

View File

@ -0,0 +1,322 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.JexlNode;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlContext;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlInfo;
import aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTJexlScript;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOptions;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTArguments;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTFunctionNode;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTIdentifier;
import aiyh.utils.tool.org.apache.commons.jexl3.parser.ASTNumberLiteral;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A Template instance.
*/
public final class TemplateScript implements JxltEngine.Template {
/** The prefix marker. */
private final String prefix;
/** The array of source blocks. */
private final TemplateEngine.Block[] source;
/** The resulting script. */
private final ASTJexlScript script;
/** The TemplateEngine expressions called by the script. */
private final TemplateEngine.TemplateExpression[] exprs;
/** The engine. */
private final TemplateEngine jxlt;
/**
* Creates a new template from an character input.
* @param engine the template engine
* @param info the source info
* @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{"
since this would preclude being able to differentiate directives and jxlt expressions
* @param reader the input reader
* @param parms the parameter names
* @throws NullPointerException if either the directive prefix or input is null
* @throws IllegalArgumentException if the directive prefix is invalid
*/
public TemplateScript(final TemplateEngine engine,
JexlInfo info,
final String directive,
final Reader reader,
final String... parms) {
if (directive == null) {
throw new NullPointerException("null prefix");
}
final String engineImmediateCharString = Character.toString(engine.getImmediateChar());
final String engineDeferredCharString = Character.toString(engine.getDeferredChar());
if (engineImmediateCharString.equals(directive)
|| engineDeferredCharString.equals(directive)
|| (engineImmediateCharString + "{").equals(directive)
|| (engineDeferredCharString + "{").equals(directive)) {
throw new IllegalArgumentException(directive + ": is not a valid directive pattern");
}
if (reader == null) {
throw new NullPointerException("null input");
}
this.jxlt = engine;
this.prefix = directive;
final List<TemplateEngine.Block> blocks = jxlt.readTemplate(prefix, reader);
final List<TemplateEngine.TemplateExpression> uexprs = new ArrayList<>();
final StringBuilder strb = new StringBuilder();
int nuexpr = 0;
int codeStart = -1;
int line = 1;
for (int b = 0; b < blocks.size(); ++b) {
final TemplateEngine.Block block = blocks.get(b);
final int bl = block.getLine();
while(line < bl) {
strb.append("//\n");
line += 1;
}
if (block.getType() == TemplateEngine.BlockType.VERBATIM) {
strb.append("jexl:print(");
strb.append(nuexpr++);
strb.append(");\n");
line += 1;
} else {
// keep track of first block of code, the frame creator
if (codeStart < 0) {
codeStart = b;
}
final String body = block.getBody();
strb.append(body);
for(int c = 0; c < body.length(); ++c) {
if (body.charAt(c) == '\n') {
line += 1;
}
}
}
}
// create the script
if (info == null) {
info = jxlt.getEngine().createInfo();
}
// allow lambda defining params
final Scope scope = parms == null ? null : new Scope(null, parms);
script = jxlt.getEngine().parse(info.at(1, 1), false, strb.toString(), scope).script();
// seek the map of expression number to scope so we can parse Unified
// expression blocks with the appropriate symbols
final Map<Integer, JexlNode.Info> minfo = new TreeMap<>();
collectPrintScope(script.script(), minfo);
// jexl:print(...) expression counter
int jpe = 0;
// create the exprs using the intended scopes
for (final TemplateEngine.Block block : blocks) {
if (block.getType() == TemplateEngine.BlockType.VERBATIM) {
final JexlNode.Info ji = minfo.get(jpe);
TemplateEngine.TemplateExpression te;
// no node info means this verbatim is surrounded by comments markers;
// expr at this index is never called
if (ji != null) {
te = jxlt.parseExpression(ji, block.getBody(), scopeOf(ji));
} else {
te = jxlt.new ConstantExpression(block.getBody(), null);
}
uexprs.add(te);
jpe += 1;
}
}
source = blocks.toArray(new TemplateEngine.Block[blocks.size()]);
exprs = uexprs.toArray(new TemplateEngine.TemplateExpression[uexprs.size()]);
}
/**
* Private ctor used to expand deferred expressions during prepare.
* @param engine the template engine
* @param thePrefix the directive prefix
* @param theSource the source
* @param theScript the script
* @param theExprs the expressions
*/
TemplateScript(final TemplateEngine engine,
final String thePrefix,
final TemplateEngine.Block[] theSource,
final ASTJexlScript theScript,
final TemplateEngine.TemplateExpression[] theExprs) {
jxlt = engine;
prefix = thePrefix;
source = theSource;
script = theScript;
exprs = theExprs;
}
/**
* Gets the scope from an info.
* @param info the node info
* @return the scope
*/
private static Scope scopeOf(final JexlNode.Info info) {
JexlNode walk = info.getNode();
while(walk != null) {
if (walk instanceof ASTJexlScript) {
return ((ASTJexlScript) walk).getScope();
}
walk = walk.jjtGetParent();
}
return null;
}
/**
* Collects the scope surrounding a call to jexl:print(i).
* <p>This allows to later parse the blocks with the known symbols
* in the frame visible to the parser.
* @param node the visited node
* @param minfo the map of printed expression number to node info
*/
private static void collectPrintScope(final JexlNode node, final Map<Integer, JexlNode.Info> minfo) {
final int nc = node.jjtGetNumChildren();
if (node instanceof ASTFunctionNode && nc == 2) {
// 0 must be the prefix jexl:
final ASTIdentifier nameNode = (ASTIdentifier) node.jjtGetChild(0);
if ("print".equals(nameNode.getName()) && "jexl".equals(nameNode.getNamespace())) {
final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
if (argNode.jjtGetNumChildren() == 1) {
// seek the epression number
final JexlNode arg0 = argNode.jjtGetChild(0);
if (arg0 instanceof ASTNumberLiteral) {
final int exprNumber = ((ASTNumberLiteral) arg0).getLiteral().intValue();
minfo.put(exprNumber, new JexlNode.Info(nameNode));
return;
}
}
}
}
for (int c = 0; c < nc; ++c) {
collectPrintScope(node.jjtGetChild(c), minfo);
}
}
/**
* @return script
*/
ASTJexlScript getScript() {
return script;
}
/**
* @return exprs
*/
TemplateEngine.TemplateExpression[] getExpressions() {
return exprs;
}
@Override
public String toString() {
final StringBuilder strb = new StringBuilder();
for (final TemplateEngine.Block block : source) {
block.toString(strb, prefix);
}
return strb.toString();
}
@Override
public String asString() {
final StringBuilder strb = new StringBuilder();
int e = 0;
for (final TemplateEngine.Block block : source) {
if (block.getType() == TemplateEngine.BlockType.DIRECTIVE) {
strb.append(prefix);
strb.append(block.getBody());
} else {
exprs[e++].asString(strb);
}
}
return strb.toString();
}
@Override
public TemplateScript prepare(final JexlContext context) {
final Engine jexl = jxlt.getEngine();
final JexlOptions options = jexl.options(script, context);
final Frame frame = script.createFrame((Object[]) null);
final TemplateInterpreter.Arguments targs = new TemplateInterpreter
.Arguments(jxlt.getEngine())
.context(context)
.options(options)
.frame(frame);
final Interpreter interpreter = new TemplateInterpreter(targs);
final TemplateEngine.TemplateExpression[] immediates = new TemplateEngine.TemplateExpression[exprs.length];
for (int e = 0; e < exprs.length; ++e) {
try {
immediates[e] = exprs[e].prepare(interpreter);
} catch (final JexlException xjexl) {
final JexlException xuel = TemplateEngine.createException(xjexl.getInfo(), "prepare", exprs[e], xjexl);
if (jexl.isSilent()) {
jexl.logger.warn(xuel.getMessage(), xuel.getCause());
return null;
}
throw xuel;
}
}
return new TemplateScript(jxlt, prefix, source, script, immediates);
}
@Override
public void evaluate(final JexlContext context, final Writer writer) {
evaluate(context, writer, (Object[]) null);
}
@Override
public void evaluate(final JexlContext context, final Writer writer, final Object... args) {
final Engine jexl = jxlt.getEngine();
final JexlOptions options = jexl.options(script, context);
final Frame frame = script.createFrame(args);
final TemplateInterpreter.Arguments targs = new TemplateInterpreter
.Arguments(jexl)
.context(context)
.options(options)
.frame(frame)
.expressions(exprs)
.writer(writer);
final Interpreter interpreter = new TemplateInterpreter(targs);
interpreter.interpret(script);
}
@Override
public Set<List<String>> getVariables() {
final Engine.VarCollector collector = jxlt.getEngine().varCollector();
for (final TemplateEngine.TemplateExpression expr : exprs) {
expr.getVariables(collector);
}
return collector.collected();
}
@Override
public String[] getParameters() {
return script.getParameters();
}
@Override
public Map<String, Object> getPragmas() {
return script.getPragmas();
}
}

View File

@ -0,0 +1,256 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
/**
* Abstract class that is used to execute an arbitrary
* method that is introspected. This is the superclass
* for all other AbstractExecutor classes.
*
* @since 1.0
*/
abstract class AbstractExecutor {
/** A marker for invocation failures in tryInvoke. */
public static final Object TRY_FAILED = JexlEngine.TRY_FAILED;
/**
* A helper to initialize the marker methods (array.get, list.get, etc...).
* @param clazz the class to introspect
* @param name the name of the method
* @param parms the parameters
* @return the method
*/
static java.lang.reflect.Method initMarker(final Class<?> clazz, final String name, final Class<?>... parms) {
try {
return clazz.getMethod(name, parms);
} catch (final Exception xnever) {
throw new Error(xnever);
}
}
/**
* Coerce an Object which must be a number to an Integer.
* @param arg the Object to coerce
* @return an Integer if it can be converted, null otherwise
*/
static Integer castInteger(final Object arg) {
return arg instanceof Number? ((Number) arg).intValue() : null;
}
/**
* Coerce an Object to a String.
* @param arg the Object to coerce
* @return a String if it can be converted, null otherwise
*/
static String castString(final Object arg) {
return arg instanceof CharSequence || arg instanceof Integer ? arg.toString() : null;
}
/**
* Creates an arguments array.
* @param args the list of arguments
* @return the arguments array
*/
static Object[] makeArgs(final Object... args) {
return args;
}
/**
* Gets the class of an object or Object if null.
* @param instance the instance
* @return the class
*/
static Class<?> classOf(final Object instance) {
return instance == null? Object.class : instance.getClass();
}
/** The class this executor applies to. */
protected final Class<?> objectClass;
/** Method to be executed. */
protected final java.lang.reflect.Method method;
/**
* Default and sole constructor.
* @param theClass the class this executor applies to
* @param theMethod the method held by this executor
*/
protected AbstractExecutor(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
objectClass = theClass;
method = theMethod;
}
@Override
public boolean equals(final Object obj) {
return this == obj || (obj instanceof AbstractExecutor && equals((AbstractExecutor) obj));
}
@Override
public int hashCode() {
return method.hashCode();
}
/**
* Indicates whether some other executor is equivalent to this one.
* @param arg the other executor to check
* @return true if both executors are equivalent, false otherwise
*/
public boolean equals(final AbstractExecutor arg) {
// common equality check
if (!this.getClass().equals(arg.getClass())) {
return false;
}
if (!this.getMethod().equals(arg.getMethod())) {
return false;
}
if (!this.getTargetClass().equals(arg.getTargetClass())) {
return false;
}
// specific equality check
final Object lhsp = this.getTargetProperty();
final Object rhsp = arg.getTargetProperty();
if (lhsp == null && rhsp == null) {
return true;
}
if (lhsp != null && rhsp != null) {
return lhsp.equals(rhsp);
}
return false;
}
/**
* Tell whether the executor is alive by looking
* at the value of the method.
*
* @return boolean Whether the executor is alive.
*/
public final boolean isAlive() {
return (method != null);
}
/**
* Specifies if this executor is cacheable and able to be reused for this
* class of object it was returned for.
*
* @return true if can be reused for this class, false if not
*/
public boolean isCacheable() {
return method != null;
}
/**
* Gets the method to be executed or used as a marker.
* @return Method The method used by execute in derived classes.
*/
public final java.lang.reflect.Method getMethod() {
return method;
}
/**
* Gets the object class targeted by this executor.
* @return the target object class
*/
public final Class<?> getTargetClass() {
return objectClass;
}
/**
* Gets the property targeted by this executor.
* @return the target property
*/
public Object getTargetProperty() {
return null;
}
/**
* Gets the method name used.
* @return method name
*/
public final String getMethodName() {
return method.getName();
}
/**
* Checks whether a tryExecute failed or not.
* @param exec the value returned by tryExecute
* @return true if tryExecute failed, false otherwise
*/
public final boolean tryFailed(final Object exec) {
return exec == JexlEngine.TRY_FAILED;
}
/**
* Abstract class that is used to execute an arbitrary 'get' method.
*/
public abstract static class Get extends AbstractExecutor implements JexlPropertyGet {
/**
* Default and sole constructor.
* @param theClass the class this executor applies to
* @param theMethod the method held by this executor
*/
protected Get(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
super(theClass, theMethod);
}
}
/**
* Abstract class that is used to execute an arbitrary 'set' method.
*/
public abstract static class Set extends AbstractExecutor implements JexlPropertySet {
/**
* Default and sole constructor.
* @param theClass the class this executor applies to
* @param theMethod the method held by this executor
*/
protected Set(final Class<?> theClass, final java.lang.reflect.Method theMethod) {
super(theClass, theMethod);
}
}
/**
* Abstract class that is used to execute an arbitrary method.
*/
public abstract static class Method extends AbstractExecutor implements JexlMethod {
/** The method key discovered from the arguments. */
protected final MethodKey key;
/**
* Creates a new instance.
* @param c the class this executor applies to
* @param m the method
* @param k the MethodKey
*/
protected Method(final Class<?> c, final java.lang.reflect.Method m, final MethodKey k) {
super(c, m);
key = k;
}
@Override
public Object getTargetProperty() {
return key;
}
@Override
public final Class<?> getReturnType() {
return method.getReturnType();
}
}
}

View File

@ -0,0 +1,98 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* <p>
* An Iterator wrapper for an Object[]. This will
* allow us to deal with all array like structures
* in a consistent manner.
* </p>
* <p>
* WARNING : this class's operations are NOT synchronized.
* It is meant to be used in a single thread, newly created
* for each use in the #foreach() directive.
* If this is used or shared, synchronize in the
* next() method.
* </p>
*
* @since 1.0
*/
public class ArrayIterator implements Iterator<Object> {
/** The objects to iterate over. */
private final Object array;
/** The size of the array. */
private final int size;
/** The current position and size in the array. */
private int pos;
/**
* Creates a new iterator instance for the specified array.
* @param arr The array for which an iterator is desired.
*/
public ArrayIterator(final Object arr) {
if (arr == null) {
array = null;
pos = 0;
size = 0;
} else if (!arr.getClass().isArray()) {
throw new IllegalArgumentException(arr.getClass() + " is not an array");
} else {
array = arr;
pos = 0;
size = Array.getLength(array);
}
}
/**
* Move to next element in the array.
*
* @return The next object in the array.
*/
@Override
public Object next() {
if (pos < size) {
return Array.get(array, pos++);
}
// we screwed up...
throw new NoSuchElementException("No more elements: " + pos
+ " / " + size);
}
/**
* Check to see if there is another element in the array.
*
* @return Whether there is another element.
*/
@Override
public boolean hasNext() {
return (pos < size);
}
/**
* No op--merely added to satify the <code>Iterator</code> interface.
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,86 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.RandomAccess;
/**
* A class that wraps an array within an AbstractList.
* <p>
* It overrides some methods because introspection uses this class a a marker for wrapped arrays; the declared class
* for these method is thus ArrayListWrapper.
* The methods are get/set/size/contains and indexOf because it is used by contains.
* </p>
*/
public class ArrayListWrapper extends AbstractList<Object> implements RandomAccess {
/** the array to wrap. */
private final Object array;
/**
* Create the wrapper.
* @param anArray {@link #array}
*/
public ArrayListWrapper(final Object anArray) {
if (!anArray.getClass().isArray()) {
throw new IllegalArgumentException(anArray.getClass() + " is not an array");
}
this.array = anArray;
}
@Override
public Object get(final int index) {
return Array.get(array, index);
}
@Override
public Object set(final int index, final Object element) {
final Object old = Array.get(array, index);
Array.set(array, index, element);
return old;
}
@Override
public int size() {
return Array.getLength(array);
}
@Override
public int indexOf(final Object o) {
final int size = size();
if (o == null) {
for (int i = 0; i < size; i++) {
if (get(i) == null) {
return i;
}
}
} else {
for (int i = 0; i < size; i++) {
if (o.equals(get(i))) {
return i;
}
}
}
return -1;
}
@Override
public boolean contains(final Object o) {
return indexOf(o) != -1;
}
}

View File

@ -0,0 +1,87 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Specialized executor to get a boolean property from an object.
* @since 2.0
*/
public final class BooleanGetExecutor extends AbstractExecutor.Get {
/** The property. */
private final String property;
/**
* Discovers a BooleanGetExecutor.
* <p>The method to be found should be named "is{P,p}property and return a boolean.</p>
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param property the property name
* @return the executor if found, null otherwise
*/
public static BooleanGetExecutor discover(final Introspector is, final Class<?> clazz, final String property) {
if (property != null && !property.isEmpty()) {
final java.lang.reflect.Method m = PropertyGetExecutor.discoverGet(is, "is", clazz, property);
if (m != null && (m.getReturnType() == Boolean.TYPE || m.getReturnType() == Boolean.class)) {
return new BooleanGetExecutor(clazz, m, property);
}
}
return null;
}
/**
* Creates an instance by attempting discovery of the get method.
* @param clazz the class to introspect
* @param method the method held by this executor
* @param key the property to get
*/
private BooleanGetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final String key) {
super(clazz, method);
property = key;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj) throws IllegalAccessException, InvocationTargetException {
return method == null ? null : method.invoke(obj, (Object[]) null);
}
@Override
public Object tryInvoke(final Object obj, final Object key) {
if (obj != null && method != null
// ensure method name matches the property name
&& property.equals(key)
&& objectClass.equals(obj.getClass())) {
try {
return method.invoke(obj, (Object[]) null);
} catch (final IllegalAccessException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,308 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import org.apache.commons.logging.Log;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A cache of introspection information for a specific class instance.
* Keys objects by an aggregation of the method name and the classes
* that make up the parameters.
* <p>
* Originally taken from the Velocity tree so we can be self-sufficient.
* </p>
*
* @see MethodKey
* @since 1.0
*/
final class ClassMap {
/**
* A method that returns itself used as a marker for cache miss,
* allows the underlying cache map to be strongly typed.
*
* @return itself as a method
*/
public static Method cacheMiss() {
try {
return ClassMap.class.getMethod("cacheMiss");
} catch (final Exception xio) {
// this really can't make an error...
return null;
}
}
/**
* The cache miss marker method.
*/
private static final Method CACHE_MISS = cacheMiss();
/**
* This is the cache to store and look up the method information.
* <p>
* It stores the association between:
* - a key made of a method name and an array of argument types.
* - a method.
* </p>
* <p>
* Since the invocation of the associated method is dynamic, there is no need (nor way) to differentiate between
* foo(int,int) and foo(Integer,Integer) since in practice only the latter form will be used through a call.
* This of course, applies to all 8 primitive types.
* </p>
* Uses ConcurrentMap since 3.0, marginally faster than 2.1 under contention.
*/
private final ConcurrentMap<MethodKey, Method> byKey = new ConcurrentHashMap<>();
/**
* Keep track of all methods with the same name; this is not modified after creation.
*/
private final Map<String, Method[]> byName = new HashMap<>();
/**
* Cache of fields.
*/
private final Map<String, Field> fieldCache;
/**
* Standard constructor.
*
* @param aClass the class to deconstruct.
* @param permissions the permissions to apply during introspection
* @param log the logger.
*/
@SuppressWarnings("LeakingThisInConstructor")
ClassMap(final Class<?> aClass, final Permissions permissions, final Log log) {
// eagerly cache methods
create(this, permissions, aClass, log);
// eagerly cache public fields
final Field[] fields = aClass.getFields();
if (fields.length > 0) {
final Map<String, Field> cache = new HashMap<>();
for (final Field field : fields) {
if (permissions.allow(field)) {
cache.put(field.getName(), field);
}
}
fieldCache = cache;
} else {
fieldCache = Collections.emptyMap();
}
}
/**
* Find a Field using its name.
*
* @param fname the field name
* @return A Field object representing the field to invoke or null.
*/
Field getField(final String fname) {
return fieldCache.get(fname);
}
/**
* Gets the field names cached by this map.
*
* @return the array of field names
*/
String[] getFieldNames() {
return fieldCache.keySet().toArray(new String[fieldCache.size()]);
}
/**
* Gets the methods names cached by this map.
*
* @return the array of method names
*/
String[] getMethodNames() {
return byName.keySet().toArray(new String[byName.size()]);
}
/**
* Gets all the methods with a given name from this map.
*
* @param methodName the seeked methods name
* @return the array of methods (null or non-empty)
*/
Method[] getMethods(final String methodName) {
final Method[] lm = byName.get(methodName);
if (lm != null && lm.length > 0) {
return lm.clone();
}
return null;
}
/**
* Find a Method using the method name and parameter objects.
* <p>
* Look in the methodMap for an entry. If found,
* it'll either be a CACHE_MISS, in which case we
* simply give up, or it'll be a Method, in which
* case, we return it.
* </p>
* <p>
* If nothing is found, then we must actually go
* and introspect the method from the MethodMap.
* </p>
*
* @param methodKey the method key
* @return A Method object representing the method to invoke or null.
* @throws MethodKey.AmbiguousException When more than one method is a match for the parameters.
*/
Method getMethod(final MethodKey methodKey) throws MethodKey.AmbiguousException {
// Look up by key
Method cacheEntry = byKey.get(methodKey);
// We looked this up before and failed.
if (cacheEntry == CACHE_MISS) {
return null;
}
if (cacheEntry == null) {
try {
// That one is expensive...
final Method[] methodList = byName.get(methodKey.getMethod());
if (methodList != null) {
cacheEntry = methodKey.getMostSpecificMethod(methodList);
}
if (cacheEntry == null) {
byKey.put(methodKey, CACHE_MISS);
} else {
byKey.put(methodKey, cacheEntry);
}
} catch (final MethodKey.AmbiguousException ae) {
// that's a miss :-)
byKey.put(methodKey, CACHE_MISS);
throw ae;
}
}
// Yes, this might just be null.
return cacheEntry;
}
/**
* Populate the Map of direct hits. These are taken from all the public methods
* that our class, its parents and their implemented interfaces provide.
*
* @param cache the ClassMap instance we create
* @param permissions the permissions to apply during introspection
* @param classToReflect the class to cache
* @param log the Log
*/
private static void create(final ClassMap cache, final Permissions permissions, Class<?> classToReflect, final Log log) {
//
// Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start
// with the actual declaring class and its interfaces and then move up (superclass etc.) until we
// hit java.lang.Object. That is important because it will give us the methods of the declaring class
// which might in turn be abstract further up the tree.
//
// We also ignore all SecurityExceptions that might happen due to SecurityManager restrictions.
//
for (; classToReflect != null; classToReflect = classToReflect.getSuperclass()) {
if (Modifier.isPublic(classToReflect.getModifiers())) {
populateWithClass(cache, permissions, classToReflect, log);
}
final Class<?>[] interfaces = classToReflect.getInterfaces();
for (final Class<?> anInterface : interfaces) {
populateWithInterface(cache, permissions, anInterface, log);
}
}
// now that we've got all methods keyed in, lets organize them by name
if (!cache.byKey.isEmpty()) {
final List<Method> lm = new ArrayList<>(cache.byKey.size());
lm.addAll(cache.byKey.values());
// sort all methods by name
lm.sort(Comparator.comparing(Method::getName));
// put all lists of methods with same name in byName cache
int start = 0;
while (start < lm.size()) {
final String name = lm.get(start).getName();
int end = start + 1;
while (end < lm.size()) {
final String walk = lm.get(end).getName();
if (!walk.equals(name)) {
break;
}
end += 1;
}
final Method[] lmn = lm.subList(start, end).toArray(new Method[end - start]);
cache.byName.put(name, lmn);
start = end;
}
}
}
/**
* Recurses up interface hierarchy to get all super interfaces.
*
* @param cache the cache to fill
* @param permissions the permissions to apply during introspection
* @param iface the interface to populate the cache from
* @param log the Log
*/
private static void populateWithInterface(final ClassMap cache,
final Permissions permissions,
final Class<?> iface,
final Log log) {
if (Modifier.isPublic(iface.getModifiers())) {
populateWithClass(cache, permissions, iface, log);
final Class<?>[] supers = iface.getInterfaces();
for (final Class<?> aSuper : supers) {
populateWithInterface(cache, permissions, aSuper, log);
}
}
}
/**
* Recurses up class hierarchy to get all super classes.
*
* @param cache the cache to fill
* @param permissions the permissions to apply during introspection
* @param clazz the class to populate the cache from
* @param log the Log
*/
private static void populateWithClass(final ClassMap cache,
final Permissions permissions,
final Class<?> clazz,
final Log log) {
try {
final Method[] methods = clazz.getDeclaredMethods();
for (final Method mi : methods) {
// add method to byKey cache; do not override
final MethodKey key = new MethodKey(mi);
final Method pmi = cache.byKey.putIfAbsent(key, permissions.allow(mi) ? mi : CACHE_MISS);
if (pmi != null && pmi != CACHE_MISS && log.isDebugEnabled() && !key.equals(new MethodKey(pmi))) {
// foo(int) and foo(Integer) have the same signature for JEXL
log.debug("Method " + pmi + " is already registered, key: " + key.debugString());
}
}
} catch (final SecurityException se) {
// Everybody feels better with...
if (log.isDebugEnabled()) {
log.debug("While accessing methods of " + clazz + ": ", se);
}
}
}
}

View File

@ -0,0 +1,121 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.beans.IntrospectionException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
/**
* A JexlMethod that wraps a constructor.
*/
public final class ConstructorMethod implements JexlMethod {
/** The wrapped constructor. */
private final Constructor<?> ctor;
/**
* Discovers a class constructor and wrap it as a JexlMethod.
* @param is the introspector
* @param ctorHandle a class or class name
* @param args constructor arguments
* @return a {@link JexlMethod}
*/
public static ConstructorMethod discover(final Introspector is, final Object ctorHandle, final Object... args) {
String className;
Class<?> clazz = null;
if (ctorHandle instanceof Class<?>) {
clazz = (Class<?>) ctorHandle;
className = clazz.getName();
} else if (ctorHandle != null) {
className = ctorHandle.toString();
} else {
return null;
}
final Constructor<?> ctor = is.getConstructor(clazz, new MethodKey(className, args));
if (ctor != null) {
return new ConstructorMethod(ctor);
}
return null;
}
/**
* Creates a constructor method.
* @param theCtor the constructor to wrap
*/
ConstructorMethod(final Constructor<?> theCtor) {
this.ctor = theCtor;
}
@Override
public Object invoke(final Object obj, final Object... params) throws Exception {
final Class<?> ctorClass = ctor.getDeclaringClass();
boolean invoke = true;
if (obj != null) {
if (obj instanceof Class<?>) {
invoke = ctorClass.equals(obj);
} else {
invoke = ctorClass.getName().equals(obj.toString());
}
}
if (invoke) {
return ctor.newInstance(params);
}
throw new IntrospectionException("constructor resolution error");
}
@Override
public Object tryInvoke(final String name, final Object obj, final Object... params) {
try {
final Class<?> ctorClass = ctor.getDeclaringClass();
boolean invoke = true;
if (obj != null) {
if (obj instanceof Class<?>) {
invoke = ctorClass.equals(obj);
} else {
invoke = ctorClass.getName().equals(obj.toString());
}
}
invoke &= name == null || ctorClass.getName().equals(name);
if (invoke) {
return ctor.newInstance(params);
}
} catch (InstantiationException | IllegalArgumentException | IllegalAccessException xinstance) {
return Uberspect.TRY_FAILED;
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
return Uberspect.TRY_FAILED;
}
@Override
public boolean tryFailed(final Object rval) {
return rval == Uberspect.TRY_FAILED;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public Class<?> getReturnType() {
return ctor.getDeclaringClass();
}
}

View File

@ -0,0 +1,91 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Specialized executor to get a property from an object.
* <p>Duck as in duck-typing for an interface like:
* <code>
* interface Get {
* Object get(Object key);
* }
* </code>
* </p>
* @since 2.0
*/
public final class DuckGetExecutor extends AbstractExecutor.Get {
/** The property, may be null. */
private final Object property;
/**
* Attempts to discover a DuckGetExecutor.
* @param is the introspector
* @param clazz the class to find the get method from
* @param identifier the key to use as an argument to the get method
* @return the executor if found, null otherwise
*/
public static DuckGetExecutor discover(final Introspector is, final Class<?> clazz, final Object identifier) {
final java.lang.reflect.Method method = is.getMethod(clazz, "get", makeArgs(identifier));
return method == null? null : new DuckGetExecutor(clazz, method, identifier);
}
/**
* Creates an instance.
* @param clazz he class the get method applies to
* @param method the method held by this executor
* @param identifier the property to get
*/
private DuckGetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object identifier) {
super(clazz, method);
property = identifier;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj) throws IllegalAccessException, InvocationTargetException {
final Object[] args = {property};
return method == null ? null : method.invoke(obj, args);
}
@Override
public Object tryInvoke(final Object obj, final Object key) {
if (obj != null
&& objectClass.equals(obj.getClass())
// ensure method name matches the property name
&& method != null
&& ((property == null && key == null)
|| (property != null && property.equals(key)))) {
try {
final Object[] args = {property};
return method.invoke(obj, args);
} catch (IllegalAccessException | IllegalArgumentException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Specialized executor to set a property of an object.
* <p>Duck as in duck-typing for an interface like:
* <code>
* interface Setable {
* Object set(Object property, Object value);
* }
* </code>
* or
* <code>
* interface Putable {
* Object put(Object property, Object value);
* }
* </code>
* </p>
* @since 2.0
*/
public final class DuckSetExecutor extends AbstractExecutor.Set {
/** The property, may be null. */
private final Object property;
/** The property value class. */
private final Class<?> valueClass;
/**
* Discovers a DuckSetExecutor.
*
* @param is the introspector
* @param clazz the class to find the set method from
* @param key the key to use as 1st argument to the set method
* @param value the value to use as 2nd argument to the set method
* @return the executor if found, null otherwise
*/
public static DuckSetExecutor discover(final Introspector is, final Class<?> clazz, final Object key, final Object value) {
java.lang.reflect.Method method = is.getMethod(clazz, "set", makeArgs(key, value));
if (method == null) {
method = is.getMethod(clazz, "put", makeArgs(key, value));
}
return method == null? null : new DuckSetExecutor(clazz, method, key, value);
}
/**
* Creates an instance.
* @param clazz the class the set method applies to
* @param method the method called through this executor
* @param key the key to use as 1st argument to the set method
* @param value the value to use as 2nd argument to the set method
*/
private DuckSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key, final Object value) {
super(clazz, method);
property = key;
valueClass = classOf(value);
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj, final Object value) throws IllegalAccessException, InvocationTargetException {
final Object[] pargs = {property, value};
if (method != null) {
method.invoke(obj, pargs);
}
return value;
}
@Override
public Object tryInvoke(final Object obj, final Object key, final Object value) {
if (obj != null
&& objectClass.equals(obj.getClass())
&& method != null
&& ((property != null && property.equals(key))
|| (property == null && key == null))
&& valueClass.equals(classOf(value))) {
try {
final Object[] args = {property, value};
method.invoke(obj, args);
return value;
} catch (IllegalAccessException | IllegalArgumentException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,59 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.util.Iterator;
import java.util.Enumeration;
/**
* An Iterator wrapper for an Enumeration.
* @param <T> the type of object this iterator returns
* @since 1.0
*/
public class EnumerationIterator<T> implements Iterator<T> {
/**
* The enumeration to iterate over.
*/
private final Enumeration<T> enumeration;
/**
* Creates a new iteratorwrapper instance for the specified
* Enumeration.
*
* @param enumer The Enumeration to wrap.
*/
public EnumerationIterator(final Enumeration<T> enumer) {
enumeration = enumer;
}
@Override
public T next() {
return enumeration.nextElement();
}
@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}
@Override
public void remove() {
// not implemented
}
}

View File

@ -0,0 +1,85 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Field;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
/**
* A JexlPropertyGet for public fields.
*/
public final class FieldGetExecutor implements JexlPropertyGet {
/**
* The public field.
*/
private final Field field;
/**
* Attempts to discover a FieldGetExecutor.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param identifier the key to use as an argument to the get method
* @return the executor if found, null otherwise
*/
public static JexlPropertyGet discover(final Introspector is, final Class<?> clazz, final String identifier) {
if (identifier != null) {
final Field field = is.getField(clazz, identifier);
if (field != null) {
return new FieldGetExecutor(field);
}
}
return null;
}
/**
* Creates a new instance of FieldPropertyGet.
* @param theField the class public field
*/
private FieldGetExecutor(final Field theField) {
field = theField;
}
@Override
public Object invoke(final Object obj) throws Exception {
return field.get(obj);
}
@Override
public Object tryInvoke(final Object obj, final Object key) {
if (obj.getClass().equals(field.getDeclaringClass()) && key.equals(field.getName())) {
try {
return field.get(obj);
} catch (final IllegalAccessException xill) {
return Uberspect.TRY_FAILED;
}
}
return Uberspect.TRY_FAILED;
}
@Override
public boolean tryFailed(final Object rval) {
return rval == Uberspect.TRY_FAILED;
}
@Override
public boolean isCacheable() {
return true;
}
}

View File

@ -0,0 +1,96 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
/**
* A JexlPropertySet for public fields.
*/
public final class FieldSetExecutor implements JexlPropertySet {
/**
* The public field.
*/
private final Field field;
/**
* Attempts to discover a FieldSetExecutor.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param identifier the key to use as an argument to the get method
* @param value the value to set the field to
* @return the executor if found, null otherwise
*/
public static JexlPropertySet discover(final Introspector is,
final Class<?> clazz,
final String identifier,
final Object value) {
if (identifier != null) {
final Field field = is.getField(clazz, identifier);
if (field != null
&& !Modifier.isFinal(field.getModifiers())
&& (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
return new FieldSetExecutor(field);
}
}
return null;
}
/**
* Creates a new instance of FieldPropertySet.
* @param theField the class public field
*/
private FieldSetExecutor(final Field theField) {
field = theField;
}
@Override
public Object invoke(final Object obj, final Object arg) throws Exception {
field.set(obj, arg);
return arg;
}
@Override
public Object tryInvoke(final Object obj, final Object key, final Object value) {
if (obj.getClass().equals(field.getDeclaringClass())
&& key.equals(field.getName())
&& (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
try {
field.set(obj, value);
return value;
} catch (final IllegalAccessException xill) {
return Uberspect.TRY_FAILED;
}
}
return Uberspect.TRY_FAILED;
}
@Override
public boolean tryFailed(final Object rval) {
return rval == Uberspect.TRY_FAILED;
}
@Override
public boolean isCacheable() {
return true;
}
}

View File

@ -0,0 +1,239 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
import java.lang.reflect.Method;
import java.beans.IntrospectionException;
/**
* Abstract an indexed property container.
* <p>This allows getting properties from expressions like <code>var.container.property</code>.
* This stores the container name and class as well as the list of available getter and setter methods.
* It implements JexlPropertyGet since such a container can only be accessed from its owning instance (not set).
*/
public final class IndexedType implements JexlPropertyGet {
/** The container name. */
private final String container;
/** The container class. */
private final Class<?> clazz;
/** The array of getter methods. */
private final Method[] getters;
/** Last get method used. */
private volatile Method get = null;
/** The array of setter methods. */
private final Method[] setters;
/** Last set method used. */
private volatile Method set = null;
/**
* Attempts to find an indexed-property getter in an object.
* The code attempts to find the list of methods getXXX() and setXXX().
* Note that this is not equivalent to the strict bean definition of indexed properties; the type of the key
* is not necessarily an int and the set/get arrays are not resolved.
*
* @param is the introspector
* @param object the object
* @param name the container name
* @return a JexlPropertyGet is successful, null otherwise
*/
public static JexlPropertyGet discover(final Introspector is, final Object object, final String name) {
if (object != null && name != null && !name.isEmpty()) {
final String base = name.substring(0, 1).toUpperCase() + name.substring(1);
final String container = name;
final Class<?> clazz = object.getClass();
final Method[] getters = is.getMethods(object.getClass(), "get" + base);
final Method[] setters = is.getMethods(object.getClass(), "set" + base);
if (getters != null) {
return new IndexedType(container, clazz, getters, setters);
}
}
return null;
}
/**
* A generic indexed property container, exposes get(key) and set(key, value)
* and solves method call dynamically based on arguments.
* <p>Must remain public for introspection purpose.</p>
*/
public static final class IndexedContainer {
/** The container instance. */
private final Object container;
/** The container type instance. */
private final IndexedType type;
/**
* Creates a new duck container.
* @param theType the container type
* @param theContainer the container instance
*/
private IndexedContainer(final IndexedType theType, final Object theContainer) {
this.type = theType;
this.container = theContainer;
}
/**
* Gets the property container name.
* @return the container name
*/
public String getContainerName() {
return type.container;
}
/**
* Gets the property container class.
* @return the container class
*/
public Class<?> getContainerClass() {
return type.clazz;
}
/**
* Gets a property from this indexed container.
* @param key the property key
* @return the property value
* @throws Exception if inner invocation fails
*/
public Object get(final Object key) throws Exception {
return type.invokeGet(container, key);
}
/**
* Sets a property in this indexed container.
* @param key the property key
* @param value the property value
* @return the invocation result (frequently null)
* @throws Exception if inner invocation fails
*/
public Object set(final Object key, final Object value) throws Exception {
return type.invokeSet(container, key, value);
}
}
/**
* Creates a new indexed property container type.
* @param name the container name
* @param c the owning class
* @param gets the array of getter methods
* @param sets the array of setter methods
*/
private IndexedType(final String name, final Class<?> c, final Method[] gets, final Method[] sets) {
this.container = name;
this.clazz = c;
this.getters = gets;
this.setters = sets;
}
@Override
public Object invoke(final Object obj) throws Exception {
if (obj != null && clazz.equals(obj.getClass())) {
return new IndexedContainer(this, obj);
}
throw new IntrospectionException("property resolution error");
}
@Override
public Object tryInvoke(final Object obj, final Object key) {
if (obj != null && key != null
&& clazz.equals(obj.getClass())
&& container.equals(key.toString())) {
return new IndexedContainer(this, obj);
}
return Uberspect.TRY_FAILED;
}
@Override
public boolean tryFailed(final Object rval) {
return rval == Uberspect.TRY_FAILED;
}
@Override
public boolean isCacheable() {
return true;
}
/**
* Gets the value of a property from a container.
* @param object the container instance (not null)
* @param key the property key (not null)
* @return the property value
* @throws Exception if invocation failed;
* IntrospectionException if a property getter could not be found
*/
private Object invokeGet(final Object object, final Object key) throws Exception {
if (getters != null && getters.length > 0) {
Method jm = get;
if (jm != null) {
final Class<?>[] ptypes = jm.getParameterTypes();
if (ptypes[0].isAssignableFrom(key.getClass())) {
return jm.invoke(object, key);
}
}
final Object[] args = {key};
final String mname = getters[0].getName();
final MethodKey km = new MethodKey(mname, args);
jm = km.getMostSpecificMethod(getters);
if (jm != null) {
final Object invoked = jm.invoke(object, args);
get = jm;
return invoked;
}
}
throw new IntrospectionException("property get error: "
+ object.getClass().toString()
+ "@" + key.toString());
}
/**
* Sets the value of a property in a container.
* @param object the container instance (not null)
* @param key the property key (not null)
* @param value the property value (not null)
* @return the result of the method invocation (frequently null)
* @throws Exception if invocation failed;
* IntrospectionException if a property setter could not be found
*/
private Object invokeSet(final Object object, final Object key, final Object value) throws Exception {
if (setters != null && setters.length > 0) {
Method jm = set;
if (jm != null) {
final Class<?>[] ptypes = jm.getParameterTypes();
if (ptypes[0].isAssignableFrom(key.getClass())
&& (value == null
|| ptypes[1].isAssignableFrom(value.getClass()))) {
return jm.invoke(object, key, value);
}
}
final Object[] args = {key, value};
final String mname = setters[0].getName();
final MethodKey km = new MethodKey(mname, args);
jm = km.getMostSpecificMethod(setters);
if (jm != null) {
final Object invoked = jm.invoke(object, args);
set = jm;
return invoked;
}
}
throw new IntrospectionException("property set error: "
+ object.getClass().toString()
+ "@" + key.toString());
}
}

View File

@ -0,0 +1,392 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import org.apache.commons.logging.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* This basic function of this class is to return a Method object for a
* particular class given the name of a method and the parameters to the method
* in the form of an Object[].
*
* <p>The first time the Introspector sees a class it creates a class method map
* for the class in question.
* Basically the class method map is a Hashtable where Method objects are keyed by the aggregation of
* the method name and the array of parameters classes.
* This mapping is performed for all the public methods of a class and stored.</p>
*
* @since 1.0
*/
public final class Introspector {
/**
* A Constructor get cache-miss.
*/
private static class CacheMiss {
/** The constructor used as cache-miss. */
@SuppressWarnings("unused")
public CacheMiss() {
}
}
/**
* The cache-miss marker for the constructors map.
*/
private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
/**
* the logger.
*/
protected final Log logger;
/**
* The class loader used to solve constructors if needed.
*/
private ClassLoader loader;
/**
* The permissions.
*/
private final Permissions permissions;
/**
* The read/write lock.
*/
private final ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* Holds the method maps for the classes we know about, keyed by Class.
*/
private final Map<Class<?>, ClassMap> classMethodMaps = new HashMap<Class<?>, ClassMap>();
/**
* Holds the map of classes ctors we know about as well as unknown ones.
*/
private final Map<MethodKey, Constructor<?>> constructorsMap = new HashMap<MethodKey, Constructor<?>>();
/**
* Holds the set of classes we have introspected.
*/
private final Map<String, Class<?>> constructibleClasses = new HashMap<String, Class<?>>();
/**
* Create the introspector.
* @param log the logger to use
* @param cloader the class loader
*/
public Introspector(final Log log, final ClassLoader cloader) {
this(log, cloader, null);
}
/**
* Create the introspector.
* @param log the logger to use
* @param cloader the class loader
* @param perms the permissions
*/
public Introspector(final Log log, final ClassLoader cloader, final Permissions perms) {
this.logger = log;
this.loader = cloader;
this.permissions = perms != null? perms : Permissions.DEFAULT;
}
/**
* Gets a class by name through this introspector class loader.
* @param className the class name
* @return the class instance or null if it could not be found
*/
public Class<?> getClassByName(final String className) {
try {
return Class.forName(className, false, loader);
} catch (final ClassNotFoundException xignore) {
return null;
}
}
/**
* Gets a method defined by a class, a name and a set of parameters.
* @param c the class
* @param name the method name
* @param params the method parameters
* @return the desired method object
* @throws MethodKey.AmbiguousException if no unambiguous method could be found through introspection
*/
public Method getMethod(final Class<?> c, final String name, final Object[] params) {
return getMethod(c, new MethodKey(name, params));
}
/**
* Gets the method defined by the <code>MethodKey</code> for the class <code>c</code>.
*
* @param c Class in which the method search is taking place
* @param key Key of the method being searched for
* @return The desired method object
* @throws MethodKey.AmbiguousException if no unambiguous method could be found through introspection
*/
public Method getMethod(final Class<?> c, final MethodKey key) {
try {
return getMap(c).getMethod(key);
} catch (final MethodKey.AmbiguousException xambiguous) {
// whoops. Ambiguous and not benign. Make a nice log message and return null...
if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) {
logger.info("ambiguous method invocation: "
+ c.getName() + "."
+ key.debugString(), xambiguous);
}
return null;
}
}
/**
* Gets the field named by <code>key</code> for the class <code>c</code>.
*
* @param c Class in which the field search is taking place
* @param key Name of the field being searched for
* @return the desired field or null if it does not exist or is not accessible
*/
public Field getField(final Class<?> c, final String key) {
return getMap(c).getField(key);
}
/**
* Gets the array of accessible field names known for a given class.
* @param c the class
* @return the class field names
*/
public String[] getFieldNames(final Class<?> c) {
if (c == null) {
return new String[0];
}
final ClassMap classMap = getMap(c);
return classMap.getFieldNames();
}
/**
* Gets the array of accessible methods names known for a given class.
* @param c the class
* @return the class method names
*/
public String[] getMethodNames(final Class<?> c) {
if (c == null) {
return new String[0];
}
final ClassMap classMap = getMap(c);
return classMap.getMethodNames();
}
/**
* Gets the array of accessible method known for a given class.
* @param c the class
* @param methodName the method name
* @return the array of methods (null or not empty)
*/
public Method[] getMethods(final Class<?> c, final String methodName) {
if (c == null) {
return null;
}
final ClassMap classMap = getMap(c);
return classMap.getMethods(methodName);
}
/**
* Gets the constructor defined by the <code>MethodKey</code>.
*
* @param key Key of the constructor being searched for
* @return The desired constructor object
* or null if no unambiguous constructor could be found through introspection.
*/
public Constructor<?> getConstructor(final MethodKey key) {
return getConstructor(null, key);
}
/**
* Gets the constructor defined by the <code>MethodKey</code>.
* @param c the class we want to instantiate
* @param key Key of the constructor being searched for
* @return The desired constructor object
* or null if no unambiguous constructor could be found through introspection.
*/
public Constructor<?> getConstructor(final Class<?> c, final MethodKey key) {
Constructor<?> ctor;
lock.readLock().lock();
try {
ctor = constructorsMap.get(key);
if (ctor != null) {
// miss or not?
return CTOR_MISS.equals(ctor) ? null : ctor;
}
} finally {
lock.readLock().unlock();
}
// let's introspect...
lock.writeLock().lock();
try {
// again for kicks
ctor = constructorsMap.get(key);
if (ctor != null) {
// miss or not?
return CTOR_MISS.equals(ctor) ? null : ctor;
}
final String cname = key.getMethod();
// do we know about this class?
Class<?> clazz = constructibleClasses.get(cname);
try {
// do find the most specific ctor
if (clazz == null) {
if (c != null && c.getName().equals(key.getMethod())) {
clazz = c;
} else {
clazz = loader.loadClass(cname);
}
// add it to list of known loaded classes
constructibleClasses.put(cname, clazz);
}
final List<Constructor<?>> l = new ArrayList<>();
for (final Constructor<?> ictor : clazz.getConstructors()) {
if (permissions.allow(ictor)) {
l.add(ictor);
}
}
// try to find one
ctor = key.getMostSpecificConstructor(l.toArray(new Constructor<?>[l.size()]));
if (ctor != null) {
constructorsMap.put(key, ctor);
} else {
constructorsMap.put(key, CTOR_MISS);
}
} catch (final ClassNotFoundException xnotfound) {
if (logger != null && logger.isDebugEnabled()) {
logger.debug("unable to find class: "
+ cname + "."
+ key.debugString(), xnotfound);
}
ctor = null;
} catch (final MethodKey.AmbiguousException xambiguous) {
if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) {
logger.info("ambiguous constructor invocation: "
+ cname + "."
+ key.debugString(), xambiguous);
}
ctor = null;
}
return ctor;
} finally {
lock.writeLock().unlock();
}
}
/**
* Gets the ClassMap for a given class.
* @param c the class
* @return the class map
*/
private ClassMap getMap(final Class<?> c) {
ClassMap classMap;
lock.readLock().lock();
try {
classMap = classMethodMaps.get(c);
} finally {
lock.readLock().unlock();
}
if (classMap == null) {
lock.writeLock().lock();
try {
// try again
classMap = classMethodMaps.get(c);
if (classMap == null) {
classMap = new ClassMap(c, permissions, logger);
classMethodMaps.put(c, classMap);
}
} finally {
lock.writeLock().unlock();
}
}
return classMap;
}
/**
* Sets the class loader used to solve constructors.
* <p>Also cleans the constructors and methods caches.</p>
* @param cloader the class loader; if null, use this instance class loader
*/
public void setLoader(ClassLoader cloader) {
final ClassLoader previous = loader;
if (cloader == null) {
cloader = getClass().getClassLoader();
}
if (!cloader.equals(loader)) {
lock.writeLock().lock();
try {
// clean up constructor and class maps
final Iterator<Map.Entry<MethodKey, Constructor<?>>> mentries = constructorsMap.entrySet().iterator();
while (mentries.hasNext()) {
final Map.Entry<MethodKey, Constructor<?>> entry = mentries.next();
final Class<?> clazz = entry.getValue().getDeclaringClass();
if (isLoadedBy(previous, clazz)) {
mentries.remove();
// the method name is the name of the class
constructibleClasses.remove(entry.getKey().getMethod());
}
}
// clean up method maps
final Iterator<Map.Entry<Class<?>, ClassMap>> centries = classMethodMaps.entrySet().iterator();
while (centries.hasNext()) {
final Map.Entry<Class<?>, ClassMap> entry = centries.next();
final Class<?> clazz = entry.getKey();
if (isLoadedBy(previous, clazz)) {
centries.remove();
}
}
loader = cloader;
} finally {
lock.writeLock().unlock();
}
}
}
/**
* Gets the class loader used by this introspector.
* @return the class loader
*/
public ClassLoader getLoader() {
return loader;
}
/**
* Checks whether a class is loaded through a given class loader or one of its ascendants.
* @param loader the class loader
* @param clazz the class to check
* @return true if clazz was loaded through the loader, false otherwise
*/
private static boolean isLoadedBy(final ClassLoader loader, final Class<?> clazz) {
if (loader != null) {
ClassLoader cloader = clazz.getClassLoader();
while (cloader != null) {
if (cloader.equals(loader)) {
return true;
}
cloader = cloader.getParent();
}
}
return false;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.util.List;
import java.lang.reflect.Array;
/**
* Specialized executor to get a property from a List or array.
* @since 2.0
*/
public final class ListGetExecutor extends AbstractExecutor.Get {
/** The java.lang.reflect.Array.get method used as an active marker in ListGet. */
private static final java.lang.reflect.Method ARRAY_GET =
initMarker(Array.class, "get", Object.class, Integer.TYPE);
/** The java.util.obj.get method used as an active marker in ListGet. */
private static final java.lang.reflect.Method LIST_GET =
initMarker(List.class, "get", Integer.TYPE);
/** The property. */
private final Integer property;
/**
* Attempts to discover a ListGetExecutor.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param index the index to use as an argument to the get method
* @return the executor if found, null otherwise
*/
public static ListGetExecutor discover(final Introspector is, final Class<?> clazz, final Integer index) {
if (index != null) {
if (clazz.isArray()) {
return new ListGetExecutor(clazz, ARRAY_GET, index);
}
if (List.class.isAssignableFrom(clazz)) {
return new ListGetExecutor(clazz, LIST_GET, index);
}
}
return null;
}
/**
* Creates an instance.
* @param clazz he class the get method applies to
* @param method the method held by this executor
* @param index the index to use as an argument to the get method
*/
private ListGetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Integer index) {
super(clazz, method);
property = index;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj) {
if (method == ARRAY_GET) {
return Array.get(obj, property);
}
return ((List<?>) obj).get(property);
}
@Override
public Object tryInvoke(final Object obj, final Object identifier) {
final Integer index = castInteger(identifier);
if (obj != null && method != null
&& objectClass.equals(obj.getClass())
&& index != null) {
if (method == ARRAY_GET) {
return Array.get(obj, index);
}
return ((List<?>) obj).get(index);
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,113 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.util.List;
import java.lang.reflect.Array;
/**
* Specialized executor to set a property in a List or array.
* @since 2.0
*/
public final class ListSetExecutor extends AbstractExecutor.Set {
/** The java.lang.reflect.Array.get method used as an active marker in ListGet. */
private static final java.lang.reflect.Method ARRAY_SET =
initMarker(Array.class, "set", Object.class, Integer.TYPE, Object.class);
/** The java.util.obj.set method used as an active marker in ListSet. */
private static final java.lang.reflect.Method LIST_SET =
initMarker(List.class, "set", Integer.TYPE, Object.class);
/** The property. */
private final Integer property;
/**
* Attempts to discover a ListSetExecutor.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param identifier the key to use as an argument to the get method
* @param value the value to use as argument in list.put(key,value)
* @return the executor if found, null otherwise
*/
public static ListSetExecutor discover(final Introspector is,
final Class<?> clazz,
final Object identifier,
final Object value) {
final Integer index = castInteger(identifier);
if (index != null) {
if (clazz.isArray()) {
// we could verify if the call can be performed but it does not change
// the fact we would fail...
// Class<?> formal = clazz.getComponentType();
// Class<?> actual = value == null? Object.class : value.getClass();
// if (IntrospectionUtils.isMethodInvocationConvertible(formal, actual, false)) {
return new ListSetExecutor(clazz, ARRAY_SET, index);
// }
}
if (List.class.isAssignableFrom(clazz)) {
return new ListSetExecutor(clazz, LIST_SET, index);
}
}
return null;
}
/**
* Creates an instance.
*
* @param clazz the class the set method applies to
* @param method the method called through this executor
* @param key the key to use as 1st argument to the set method
*/
private ListSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Integer key) {
super(clazz, method);
property = key;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj, final Object value) {
if (method == ARRAY_SET) {
Array.set(obj, property, value);
} else {
@SuppressWarnings("unchecked") // LSE should only be created for array or list types
final List<Object> list = (List<Object>) obj;
list.set(property, value);
}
return value;
}
@Override
public Object tryInvoke(final Object obj, final Object key, final Object value) {
final Integer index = castInteger(key);
if (obj != null && method != null
&& objectClass.equals(obj.getClass())
&& index != null) {
if (method == ARRAY_SET) {
Array.set(obj, index, value);
} else {
@SuppressWarnings("unchecked") // LSE should only be created for array or list types
final List<Object> list = (List<Object>) obj;
list.set(index, value);
}
return value;
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.util.Map;
/**
* Specialized executor to get a property from a Map.
* @since 2.0
*/
public final class MapGetExecutor extends AbstractExecutor.Get {
/** The java.util.map.get method used as an active marker in MapGet. */
private static final java.lang.reflect.Method MAP_GET =
initMarker(Map.class, "get", Object.class);
/** The property. */
private final Object property;
/**
* Attempts to discover a MapGetExecutor.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param identifier the key to use as an argument to the get method
* @return the executor if found, null otherwise
*/
public static MapGetExecutor discover(final Introspector is, final Class<?> clazz, final Object identifier) {
if (Map.class.isAssignableFrom(clazz)) {
return new MapGetExecutor(clazz, MAP_GET, identifier);
}
return null;
}
/**
* Creates an instance.
* @param clazz he class the get method applies to
* @param method the method held by this executor
* @param key the property to get
*/
private MapGetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key) {
super(clazz, method);
property = key;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj) {
@SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
final Map<Object, ?> map = (Map<Object, ?>) obj;
return map.get(property);
}
@Override
public Object tryInvoke(final Object obj, final Object key) {
if (obj != null
&& method != null
&& objectClass.equals(obj.getClass())
&& ((property == null && key == null)
|| (property != null && key != null && property.getClass().equals(key.getClass())))) {
@SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
final Map<Object, ?> map = (Map<Object, ?>) obj;
return map.get(key);
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.util.Map;
/**
* Specialized executor to set a property in a Map.
* @since 2.0
*/
public final class MapSetExecutor extends AbstractExecutor.Set {
/** The java.util.map.put method used as an active marker in MapSet. */
private static final java.lang.reflect.Method MAP_SET = initMarker(Map.class, "put", Object.class, Object.class);
/** The property. */
private final Object property;
/** The property value class. */
private final Class<?> valueClass;
/**
* Attempts to discover a MapSetExecutor.
*
* @param is the introspector
* @param clazz the class to find the set method from
* @param identifier the key to use as an argument to the get method
* @param value the value to use as argument in map.put(key,value)
* @return the executor if found, null otherwise
*/
public static MapSetExecutor discover(final Introspector is,
final Class<?> clazz,
final Object identifier,
final Object value) {
if (Map.class.isAssignableFrom(clazz)) {
return new MapSetExecutor(clazz, MAP_SET, identifier, value);
}
return null;
}
/**
* Creates an instance.
* @param clazz the class the set method applies to
* @param method the method called through this executor
* @param key the key to use as 1st argument to the set method
* @param value the value to use as 2nd argument to the set method
*/
private MapSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Object key, final Object value) {
super(clazz, method);
property = key;
valueClass = classOf(value);
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object obj, final Object value) {
@SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
final Map<Object,Object> map = ((Map<Object, Object>) obj);
map.put(property, value);
return value;
}
@Override
public Object tryInvoke(final Object obj, final Object key, final Object value) {
if (obj != null
&& method != null
&& objectClass.equals(obj.getClass())
&& ((property == null && key == null)
|| (property != null && key != null && property.getClass().equals(key.getClass())))
&& valueClass.equals(classOf(value))) {
@SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
final Map<Object,Object> map = ((Map<Object, Object>) obj);
map.put(key, value);
return value;
}
return TRY_FAILED;
}
}

View File

@ -0,0 +1,157 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
/**
* Specialized executor to invoke a method on an object.
* @since 2.0
*/
public final class MethodExecutor extends AbstractExecutor.Method {
/** If this method is a vararg method, vaStart is the last argument index. */
private final int vaStart;
/** If this method is a vararg method, vaClass is the component type of the vararg array. */
private final Class<?> vaClass;
/**
* Discovers a {@link MethodExecutor}.
* <p>
* If the object is an array, an attempt will be made to find the
* method in a List (see {@link ArrayListWrapper})
* </p>
* <p>
* If the object is a class, an attempt will be made to find the
* method as a static method of that class.
* </p>
* @param is the introspector used to discover the method
* @param obj the object to introspect
* @param method the name of the method to find
* @param args the method arguments
* @return a filled up parameter (may contain a null method)
*/
public static MethodExecutor discover(final Introspector is, final Object obj, final String method, final Object[] args) {
final Class<?> clazz = obj.getClass();
final MethodKey key = new MethodKey(method, args);
java.lang.reflect.Method m = is.getMethod(clazz, key);
if (m == null && clazz.isArray()) {
// check for support via our array->list wrapper
m = is.getMethod(ArrayListWrapper.class, key);
}
if (m == null && obj instanceof Class<?>) {
m = is.getMethod((Class<?>) obj, key);
}
return m == null? null : new MethodExecutor(clazz, m, key);
}
/**
* Creates a new instance.
* @param c the class this executor applies to
* @param m the method
* @param k the MethodKey
*/
private MethodExecutor(final Class<?> c, final java.lang.reflect.Method m, final MethodKey k) {
super(c, m, k);
int vastart = -1;
Class<?> vaclass = null;
if (MethodKey.isVarArgs(method)) {
// if the last parameter is an array, the method is considered as vararg
final Class<?>[] formal = method.getParameterTypes();
vastart = formal.length - 1;
vaclass = formal[vastart].getComponentType();
}
vaStart = vastart;
vaClass = vaclass;
}
@Override
public Object invoke(final Object o, Object... args) throws IllegalAccessException, InvocationTargetException {
if (vaClass != null && args != null) {
args = handleVarArg(args);
}
if (method.getDeclaringClass() == ArrayListWrapper.class && o.getClass().isArray()) {
return method.invoke(new ArrayListWrapper(o), args);
}
return method.invoke(o, args);
}
@Override
public Object tryInvoke(final String name, final Object obj, final Object... args) {
final MethodKey tkey = new MethodKey(name, args);
// let's assume that invocation will fly if the declaring class is the
// same and arguments have the same type
if (objectClass.equals(obj.getClass()) && tkey.equals(key)) {
try {
return invoke(obj, args);
} catch (IllegalAccessException | IllegalArgumentException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return JexlEngine.TRY_FAILED;
}
/**
* Reassembles arguments if the method is a vararg method.
* @param actual The actual arguments being passed to this method
* @return The actual parameters adjusted for the varargs in order
* to fit the method declaration.
*/
@SuppressWarnings("SuspiciousSystemArraycopy")
private Object[] handleVarArg(Object[] actual) {
final Class<?> vaclass = vaClass;
final int vastart = vaStart;
// variable arguments count
final int varargc = actual.length - vastart;
// if no values are being passed into the vararg, size == 0
if (varargc == 1) {
// if one non-null value is being passed into the vararg,
// and that arg is not the sole argument and not an array of the expected type,
// make the last arg an array of the expected type
if (actual[vastart] != null) {
final Class<?> aclazz = actual[vastart].getClass();
if (!aclazz.isArray() || !vaclass.isAssignableFrom(aclazz.getComponentType())) {
// create a 1-length array to hold and replace the last argument
final Object lastActual = Array.newInstance(vaclass, 1);
Array.set(lastActual, 0, actual[vastart]);
actual[vastart] = lastActual;
}
}
// else, the vararg is null and used as is, considered as T[]
} else {
// if no or multiple values are being passed into the vararg,
// put them in an array of the expected type
final Object varargs = Array.newInstance(vaclass, varargc);
System.arraycopy(actual, vastart, varargs, 0, varargc);
// put all arguments into a new actual array of the appropriate size
final Object[] newActual = new Object[vastart + 1];
System.arraycopy(actual, 0, newActual, 0, vastart);
newActual[vastart] = varargs;
// replace the old actual array
actual = newActual;
}
return actual;
}
}

View File

@ -0,0 +1,840 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
/**
* A method key usable by the introspector cache.
* <p>
* This stores a method (or class) name and parameters.
* </p>
* <p>
* This replaces the original key scheme which used to build the key
* by concatenating the method name and parameters class names as one string
* with the exception that primitive types were converted to their object class equivalents.
* </p>
* <p>
* The key is still based on the same information, it is just wrapped in an object instead.
* Primitive type classes are converted to they object equivalent to make a key;
* int foo(int) and int foo(Integer) do generate the same key.
* </p>
* A key can be constructed either from arguments (array of objects) or from parameters
* (array of class).
* Roughly 3x faster than string key to access the map and uses less memory.
*/
public final class MethodKey {
/** The initial size of the primitive conversion map. */
private static final int PRIMITIVE_SIZE = 11;
/** The hash code. */
private final int hashCode;
/** The method name. */
private final String method;
/** The parameters. */
private final Class<?>[] params;
/** A marker for empty parameter list. */
private static final Class<?>[] NOARGS = new Class<?>[0];
/** The hash code constants. */
private static final int HASH = 37;
/**
* Creates a key from a method name and a set of arguments.
*
* @param aMethod the method to generate the key from
* @param args the intended method arguments
*/
public MethodKey(final String aMethod, final Object[] args) {
// !! keep this in sync with the other ctor (hash code) !!
this.method = aMethod;
int hash = this.method.hashCode();
final int size;
// CSOFF: InnerAssignment
if (args != null && (size = args.length) > 0) {
this.params = new Class<?>[size];
for (int p = 0; p < size; ++p) {
final Object arg = args[p];
// null arguments use void as Void.class as marker
final Class<?> parm = arg == null ? Void.class : arg.getClass();
hash = (HASH * hash) + parm.hashCode();
this.params[p] = parm;
}
} else {
this.params = NOARGS;
}
this.hashCode = hash;
}
/**
* Creates a key from a method.
*
* @param aMethod the method to generate the key from.
*/
MethodKey(final Method aMethod) {
this(aMethod.getName(), aMethod.getParameterTypes());
}
/**
* Creates a key from a constructor.
*
* @param aCtor the constructor to generate the key from.
*/
MethodKey(final Constructor<?> aCtor) {
this(aCtor.getDeclaringClass().getName(), aCtor.getParameterTypes());
}
/**
* Creates a key from a method name and a set of parameters.
*
* @param aMethod the method to generate the key from
* @param args the intended method parameters
*/
MethodKey(final String aMethod, final Class<?>[] args) {
// !! keep this in sync with the other ctor (hash code) !!
this.method = aMethod.intern();
int hash = this.method.hashCode();
final int size;
// CSOFF: InnerAssignment
if (args != null && (size = args.length) > 0) {
this.params = new Class<?>[size];
for (int p = 0; p < size; ++p) {
final Class<?> parm = primitiveClass(args[p]);
hash = (HASH * hash) + parm.hashCode();
this.params[p] = parm;
}
} else {
this.params = NOARGS;
}
this.hashCode = hash;
}
/**
* Gets this key's method name.
*
* @return the method name
*/
String getMethod() {
return method;
}
/**
* Gets this key's method parameter classes.
*
* @return the parameters
*/
Class<?>[] getParameters() {
return params;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof MethodKey) {
final MethodKey key = (MethodKey) obj;
return method.equals(key.method) && Arrays.equals(params, key.params);
}
return false;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder(method);
for (final Class<?> c : params) {
builder.append(c == Void.class ? "null" : c.getName());
}
return builder.toString();
}
/**
* Outputs a human readable debug representation of this key.
*
* @return method(p0, p1, ...)
*/
public String debugString() {
final StringBuilder builder = new StringBuilder(method);
builder.append('(');
for (int i = 0; i < params.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(Void.class == params[i] ? "null" : params[i].getName());
}
builder.append(')');
return builder.toString();
}
/**
* Checks whether a method accepts a variable number of arguments.
* <p>May be due to a subtle bug in some JVMs, if a varargs method is an override, depending on (may be) the
* class introspection order, the isVarargs flag on the method itself will be false.
* To circumvent the potential problem, fetch the method with the same signature from the super-classes,
* - which will be different if override -and get the varargs flag from it.
*
* @param method the method to check for varargs
* @return true if declared varargs, false otherwise
*/
public static boolean isVarArgs(final Method method) {
if (method == null) {
return false;
}
if (method.isVarArgs()) {
return true;
}
// before climbing up the hierarchy, verify that the last parameter is an array
final Class<?>[] ptypes = method.getParameterTypes();
if (ptypes.length == 0 || ptypes[ptypes.length - 1].getComponentType() == null) {
return false;
}
final String mname = method.getName();
// if this is an override, was it actually declared as varargs?
Class<?> clazz = method.getDeclaringClass();
do {
try {
final Method m = clazz.getMethod(mname, ptypes);
if (m.isVarArgs()) {
return true;
}
} catch (final NoSuchMethodException xignore) {
// this should not happen...
}
clazz = clazz.getSuperclass();
} while (clazz != null);
return false;
}
/**
* Gets the most specific method that is applicable to the parameters of this key.
*
* @param methods a list of methods.
* @return the most specific method.
* @throws AmbiguousException if there is more than one.
*/
public Method getMostSpecificMethod(final Method[] methods) {
return METHODS.getMostSpecific(this, methods);
}
/**
* Gets the most specific constructor that is applicable to the parameters of this key.
*
* @param methods a list of constructors.
* @return the most specific constructor.
* @throws AmbiguousException if there is more than one.
*/
public Constructor<?> getMostSpecificConstructor(final Constructor<?>[] methods) {
return CONSTRUCTORS.getMostSpecific(this, methods);
}
/**
* Determines whether a type represented by a class object is
* convertible to another type represented by a class object using a
* method invocation conversion, treating object types of primitive
* types as if they were primitive types (that is, a Boolean actual
* parameter type matches boolean primitive formal type). This behavior
* is because this method is used to determine applicable methods for
* an actual parameter list, and primitive types are represented by
* their object duals in reflective method calls.
*
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @param possibleVarArg whether or not we're dealing with the last parameter
* in the method declaration
* @return true if either formal type is assignable from actual type,
* or formal is a primitive type and actual is its corresponding object
* type or an object type of a primitive type that can be converted to
* the formal type.
*/
public static boolean isInvocationConvertible(final Class<?> formal,
final Class<?> actual,
final boolean possibleVarArg) {
return isInvocationConvertible(formal, actual, false, possibleVarArg);
}
/**
* Determines whether a type represented by a class object is
* convertible to another type represented by a class object using a
* method invocation conversion, without matching object and primitive
* types. This method is used to determine the more specific type when
* comparing signatures of methods.
*
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @param possibleVarArg whether or not we're dealing with the last parameter
* in the method declaration
* @return true if either formal type is assignable from actual type,
* or formal and actual are both primitive types and actual can be
* subject to widening conversion to formal.
*/
public static boolean isStrictInvocationConvertible(final Class<?> formal,
final Class<?> actual,
final boolean possibleVarArg) {
return isInvocationConvertible(formal, actual, true, possibleVarArg);
}
/**
* Converts a primitive type to its corresponding class.
* <p>
* If the argument type is primitive then we want to convert our
* primitive type signature to the corresponding Object type so
* introspection for methods with primitive types will work
* correctly.
* </p>
*
* @param parm a may-be primitive type class
* @return the equivalent object class
*/
static Class<?> primitiveClass(final Class<?> parm) {
// it was marginally faster to get from the map than call isPrimitive...
// if (!parm.isPrimitive()) return parm;
final Class<?>[] prim = CONVERTIBLES.get(parm);
return prim == null ? parm : prim[0];
}
/**
* Helper to build class arrays.
*
* @param args the classes
* @return the array
*/
private static Class<?>[] asArray(final Class<?>... args) {
return args;
}
/**
* Maps from primitive types to invocation compatible classes.
* <p>Considering the key as a parameter type, the value is the list of argument classes that are invocation
* compatible with the parameter. Example is Long is invocation convertible to long.
*/
private static final Map<Class<?>, Class<?>[]> CONVERTIBLES;
static {
CONVERTIBLES = new HashMap<Class<?>, Class<?>[]>(PRIMITIVE_SIZE);
CONVERTIBLES.put(Boolean.TYPE,
asArray(Boolean.class));
CONVERTIBLES.put(Character.TYPE,
asArray(Character.class));
CONVERTIBLES.put(Byte.TYPE,
asArray(Byte.class));
CONVERTIBLES.put(Short.TYPE,
asArray(Short.class, Byte.class));
CONVERTIBLES.put(Integer.TYPE,
asArray(Integer.class, Short.class, Byte.class));
CONVERTIBLES.put(Long.TYPE,
asArray(Long.class, Integer.class, Short.class, Byte.class));
CONVERTIBLES.put(Float.TYPE,
asArray(Float.class, Long.class, Integer.class, Short.class, Byte.class));
CONVERTIBLES.put(Double.TYPE,
asArray(Double.class, Float.class, Long.class, Integer.class, Short.class, Byte.class));
}
/**
* Maps from primitive types to invocation compatible primitive types.
* <p>Considering the key as a parameter type, the value is the list of argument types that are invocation
* compatible with the parameter. Example is 'int' is invocation convertible to 'long'.
*/
private static final Map<Class<?>, Class<?>[]> STRICT_CONVERTIBLES;
static {
STRICT_CONVERTIBLES = new HashMap<Class<?>, Class<?>[]>(PRIMITIVE_SIZE);
STRICT_CONVERTIBLES.put(Short.TYPE,
asArray(Byte.TYPE));
STRICT_CONVERTIBLES.put(Integer.TYPE,
asArray(Short.TYPE, Byte.TYPE));
STRICT_CONVERTIBLES.put(Long.TYPE,
asArray(Integer.TYPE, Short.TYPE, Byte.TYPE));
STRICT_CONVERTIBLES.put(Float.TYPE,
asArray(Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE));
STRICT_CONVERTIBLES.put(Double.TYPE,
asArray(Float.TYPE, Long.TYPE, Integer.TYPE, Short.TYPE, Byte.TYPE));
}
/**
* Determines parameter-argument invocation compatibility.
*
* @param formal the formal parameter type
* @param actual the argument type
* @param strict whether the check is strict or not
* @param possibleVarArg whether or not we're dealing with the last parameter in the method declaration
* @return true if compatible, false otherwise
*/
private static boolean isInvocationConvertible(
final Class<?> formal, Class<?> actual, final boolean strict, final boolean possibleVarArg) {
/* if it's a null, it means the arg was null */
if (actual == null && !formal.isPrimitive()) {
return true;
}
/* system asssignable, both sides must be array or not */
if (actual != null && formal.isAssignableFrom(actual) && actual.isArray() == formal.isArray()) {
return true;
}
/* catch all... */
if (!strict && formal == Object.class) {
return true;
}
/* Primitive conversion check. */
if (formal.isPrimitive()) {
final Class<?>[] clist = strict ? STRICT_CONVERTIBLES.get(formal) : CONVERTIBLES.get(formal);
if (clist != null) {
for (final Class<?> aClass : clist) {
if (actual == aClass) {
return true;
}
}
}
return false;
}
/* Check for vararg conversion. */
if (possibleVarArg && formal.isArray()) {
if (actual != null && actual.isArray()) {
actual = actual.getComponentType();
}
return isInvocationConvertible(formal.getComponentType(), actual, strict, false);
}
return false;
}
/**
* whether a method/ctor is more specific than a previously compared one.
*/
private static final int MORE_SPECIFIC = 0;
/**
* whether a method/ctor is less specific than a previously compared one.
*/
private static final int LESS_SPECIFIC = 1;
/**
* A method/ctor doesn't match a previously compared one.
*/
private static final int INCOMPARABLE = 2;
/**
* Simple distinguishable exception, used when
* we run across ambiguous overloading. Caught
* by the introspector.
*/
public static class AmbiguousException extends RuntimeException {
/** Version Id for serializable. */
private static final long serialVersionUID = -201801091655L;
/** Whether this exception should be considered severe. */
private final boolean severe;
/**
* A severe or not ambiguous exception.
*
* @param flag logging flag
*/
AmbiguousException(final boolean flag) {
this.severe = flag;
}
/**
* Whether this exception is considered severe or benign.
* <p>Note that this is meant in the context of an ambiguous exception; benign cases can only be triggered
* by null arguments often related to runtime problems (not simply on overload signatures).
*
* @return true if severe, false if benign.
*/
public boolean isSevere() {
return severe;
}
}
/**
* Utility for parameters matching.
*
* @param <T> Method or Constructor
*/
private abstract static class Parameters<T> {
/**
* Extract the parameter types from its applicable argument.
*
* @param app a method or constructor
* @return the parameters
*/
protected abstract Class<?>[] getParameterTypes(T app);
/**
* Whether a constructor or method handles varargs.
*
* @param app the constructor or method
* @return true if varargs, false otherwise
*/
protected abstract boolean isVarArgs(T app);
// CSOFF: RedundantThrows
/**
* Gets the most specific method that is applicable to actual argument types.<p>
* Attempts to find the most specific applicable method using the
* algorithm described in the JLS section 15.12.2 (with the exception that it can't
* distinguish a primitive type argument from an object type argument, since in reflection
* primitive type arguments are represented by their object counterparts, so for an argument of
* type (say) java.lang.Integer, it will not be able to decide between a method that takes int and a
* method that takes java.lang.Integer as a parameter.
* </p>
* <p>
* This turns out to be a relatively rare case where this is needed - however, functionality
* like this is needed.
* </p>
*
* @param key a method key, esp its parameters
* @param methods a list of methods
* @return the most specific method.
* @throws AmbiguousException if there is more than one.
*/
private T getMostSpecific(final MethodKey key, final T[] methods) {
final Class<?>[] args = key.params;
final LinkedList<T> applicables = getApplicables(methods, args);
if (applicables.isEmpty()) {
return null;
}
if (applicables.size() == 1) {
return applicables.getFirst();
}
/*
* This list will contain the maximally specific methods. Hopefully at
* the end of the below loop, the list will contain exactly one method,
* (the most specific method) otherwise we have ambiguity.
*/
final LinkedList<T> maximals = new LinkedList<T>();
for (final T app : applicables) {
final Class<?>[] parms = getParameterTypes(app);
boolean lessSpecific = false;
final Iterator<T> maximal = maximals.iterator();
while (!lessSpecific && maximal.hasNext()) {
final T max = maximal.next();
switch (moreSpecific(args, parms, getParameterTypes(max))) {
case MORE_SPECIFIC:
/*
* This method is more specific than the previously
* known maximally specific, so remove the old maximum.
*/
maximal.remove();
break;
case LESS_SPECIFIC:
/*
* This method is less specific than some of the
* currently known maximally specific methods, so we
* won't add it into the set of maximally specific
* methods
*/
lessSpecific = true;
break;
default:
// nothing do do
}
}
if (!lessSpecific) {
maximals.addLast(app);
}
}
// if we have more than one maximally specific method, this call is ambiguous...
if (maximals.size() > 1) {
throw ambiguousException(args, applicables);
}
return maximals.getFirst();
} // CSON: RedundantThrows
/**
* Creates an ambiguous exception.
* <p>
* This method computes the severity of the ambiguity. The only <em>non-severe</em> case is when there is
* at least one null argument and at most one applicable method or constructor has a corresponding 'Object'
* parameter.
* We thus consider that ambiguity is benign in presence of null arguments but in the case where
* the corresponding parameter is of type Object in more than one applicable overloads.
* <p>
* Rephrasing:
* <ul>
* <li>If all arguments are valid instances - no null argument -, ambiguity is severe.</li>
* <li>If there is at least one null argument, the ambiguity is severe if more than one method has a
* corresponding parameter of class 'Object'.</li>
* </ul>
*
* @param classes the argument args
* @param applicables the list of applicable methods or constructors
* @return an ambiguous exception
*/
private AmbiguousException ambiguousException(final Class<?>[] classes, final List<T> applicables) {
boolean severe = false;
int instanceArgCount = 0; // count the number of valid instances, aka not null
for (int c = 0; c < classes.length; ++c) {
final Class<?> argClazz = classes[c];
if (Void.class.equals(argClazz)) {
// count the number of methods for which the current arg maps to an Object parameter
int objectParmCount = 0;
for (final T app : applicables) {
final Class<?>[] parmClasses = getParameterTypes(app);
final Class<?> parmClass = parmClasses[c];
if (Object.class.equals(parmClass) && (objectParmCount++ == 2)) {
severe = true;
break;
}
}
} else {
instanceArgCount += 1;
}
}
return new AmbiguousException(severe || instanceArgCount == classes.length);
}
/**
* Determines which method signature (represented by a class array) is more
* specific. This defines a partial ordering on the method signatures.
*
* @param a the arguments signature
* @param c1 first method signature to compare
* @param c2 second method signature to compare
* @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
* c1 is less specific than c2, INCOMPARABLE if they are incomparable.
*/
private int moreSpecific(final Class<?>[] a, final Class<?>[] c1, final Class<?>[] c2) {
// compare lengths to handle comparisons where the size of the arrays
// doesn't match, but the methods are both applicable due to the fact
// that one is a varargs method
if (c1.length > a.length) {
return LESS_SPECIFIC;
}
if (c2.length > a.length) {
return MORE_SPECIFIC;
}
if (c1.length > c2.length) {
return MORE_SPECIFIC;
}
if (c2.length > c1.length) {
return LESS_SPECIFIC;
}
// same length, keep ultimate param offset for vararg checks
final int length = c1.length;
final int ultimate = c1.length - 1;
// ok, move on and compare those of equal lengths
for (int i = 0; i < length; ++i) {
if (c1[i] != c2[i]) {
final boolean last = (i == ultimate);
// argument is null, prefer an Object param
if (a[i] == Void.class) {
if (c1[i] == Object.class && c2[i] != Object.class) {
return MORE_SPECIFIC;
}
if (c1[i] != Object.class && c2[i] == Object.class) {
return LESS_SPECIFIC;
}
}
// prefer primitive on non null arg, non primitive otherwise
boolean c1s = isPrimitive(c1[i], last);
boolean c2s = isPrimitive(c2[i], last);
if (c1s != c2s) {
return (c1s == (a[i] != Void.class)) ? MORE_SPECIFIC : LESS_SPECIFIC;
}
// if c2 can be converted to c1 but not the opposite,
// c1 is more specific than c2
c1s = isStrictConvertible(c2[i], c1[i], last);
c2s = isStrictConvertible(c1[i], c2[i], last);
if (c1s != c2s) {
return c1s ? MORE_SPECIFIC : LESS_SPECIFIC;
}
}
}
// Incomparable due to non-related arguments (i.e.foo(Runnable) vs. foo(Serializable))
return INCOMPARABLE;
}
/**
* Checks whether a parameter class is a primitive.
*
* @param c the parameter class
* @param possibleVarArg true if this is the last parameter which can be a primitive array (vararg call)
* @return true if primitive, false otherwise
*/
private boolean isPrimitive(final Class<?> c, final boolean possibleVarArg) {
if (c != null) {
if (c.isPrimitive()) {
return true;
}
if (possibleVarArg) {
final Class<?> t = c.getComponentType();
return t != null && t.isPrimitive();
}
}
return false;
}
/**
* Returns all methods that are applicable to actual argument types.
*
* @param methods list of all candidate methods
* @param classes the actual types of the arguments
* @return a list that contains only applicable methods (number of
* formal and actual arguments matches, and argument types are assignable
* to formal types through a method invocation conversion).
*/
private LinkedList<T> getApplicables(final T[] methods, final Class<?>[] classes) {
final LinkedList<T> list = new LinkedList<T>();
for (final T method : methods) {
if (isApplicable(method, classes)) {
list.add(method);
}
}
return list;
}
/**
* Returns true if the supplied method is applicable to actual
* argument types.
*
* @param method method that will be called
* @param actuals arguments signature for method
* @return true if method is applicable to arguments
*/
private boolean isApplicable(final T method, final Class<?>[] actuals) {
final Class<?>[] formals = getParameterTypes(method);
// if same number or args or
// there's just one more methodArg than class arg
// and the last methodArg is an array, then treat it as a vararg
if (formals.length == actuals.length) {
// this will properly match when the last methodArg
// is an array/varargs and the last class is the type of array
// (e.g. String when the method is expecting String...)
for (int i = 0; i < actuals.length; ++i) {
if (!isConvertible(formals[i], actuals[i], false)) {
// if we're on the last arg and the method expects an array
if (i == actuals.length - 1 && formals[i].isArray()) {
// check to see if the last arg is convertible
// to the array's component type
return isConvertible(formals[i], actuals[i], true);
}
return false;
}
}
return true;
}
// number of formal and actual differ, method must be vararg
if (!isVarArgs(method)) {
return false;
}
// less arguments than method parameters: vararg is null
if (formals.length > actuals.length) {
// only one parameter, the last (ie vararg) can be missing
if (formals.length - actuals.length > 1) {
return false;
}
// check that all present args match up to the method parms
for (int i = 0; i < actuals.length; ++i) {
if (!isConvertible(formals[i], actuals[i], false)) {
return false;
}
}
return true;
}
// more arguments given than the method accepts; check for varargs
if (formals.length > 0 && actuals.length > 0) {
// check that they all match up to the last method arg
for (int i = 0; i < formals.length - 1; ++i) {
if (!isConvertible(formals[i], actuals[i], false)) {
return false;
}
}
// check that all remaining arguments are convertible to the vararg type
// (last parm is an array since method is vararg)
final Class<?> vararg = formals[formals.length - 1].getComponentType();
for (int i = formals.length - 1; i < actuals.length; ++i) {
if (!isConvertible(vararg, actuals[i], false)) {
return false;
}
}
return true;
}
// no match
return false;
}
/**
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @param possibleVarArg whether or not we're dealing with the last parameter
* in the method declaration
* @return see isMethodInvocationConvertible.
* @see #isInvocationConvertible(Class, Class, boolean)
*/
private boolean isConvertible(final Class<?> formal, final Class<?> actual, final boolean possibleVarArg) {
// if we see Void.class, the argument was null
return isInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg);
}
/**
* @param formal the formal parameter type to which the actual
* parameter type should be convertible
* @param actual the actual parameter type.
* @param possibleVarArg whether or not we're dealing with the last parameter
* in the method declaration
* @return see isStrictMethodInvocationConvertible.
* @see #isStrictInvocationConvertible(Class, Class, boolean)
*/
private boolean isStrictConvertible(final Class<?> formal, final Class<?> actual,
final boolean possibleVarArg) {
// if we see Void.class, the argument was null
return isStrictInvocationConvertible(formal, actual.equals(Void.class) ? null : actual, possibleVarArg);
}
}
/**
* The parameter matching service for methods.
*/
private static final Parameters<Method> METHODS = new Parameters<Method>() {
@Override
protected Class<?>[] getParameterTypes(final Method app) {
return app.getParameterTypes();
}
@Override
public boolean isVarArgs(final Method app) {
return MethodKey.isVarArgs(app);
}
};
/**
* The parameter matching service for constructors.
*/
private static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() {
@Override
protected Class<?>[] getParameterTypes(final Constructor<?> app) {
return app.getParameterTypes();
}
@Override
public boolean isVarArgs(final Constructor<?> app) {
return app.isVarArgs();
}
};
}

View File

@ -0,0 +1,224 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import aiyh.utils.tool.org.apache.commons.jexl3.annotations.NoJexl;
/**
* Checks whether an element (ctor, field or method) is visible by JEXL introspection.
* Default implementation does this by checking if element has been annotated with NoJexl.
*/
public class Permissions {
/** Allow inheritance. */
protected Permissions() {
}
/**
* The default singleton.
*/
public static final Permissions DEFAULT = new Permissions();
/**
* Checks whether a package explicitly disallows JEXL introspection.
* @param pack the package
* @return true if JEXL is allowed to introspect, false otherwise
*/
public boolean allow(final Package pack) {
if (pack == null || pack.getAnnotation(NoJexl.class) != null) {
return false;
}
return true;
}
/**
* Checks whether a class or one of its super-classes or implemented interfaces
* explicitly disallows JEXL introspection.
* @param clazz the class to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
public boolean allow(final Class<?> clazz) {
return clazz != null && allow(clazz.getPackage()) && allow(clazz, true);
}
/**
* Checks whether a constructor explicitly disallows JEXL introspection.
* @param ctor the constructor to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
public boolean allow(final Constructor<?> ctor) {
if (ctor == null) {
return false;
}
if (!Modifier.isPublic(ctor.getModifiers())) {
return false;
}
final Class<?> clazz = ctor.getDeclaringClass();
if (!allow(clazz, false)) {
return false;
}
// is ctor annotated with nojexl ?
final NoJexl nojexl = ctor.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
return true;
}
/**
* Checks whether a field explicitly disallows JEXL introspection.
* @param field the field to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
public boolean allow(final Field field) {
if (field == null) {
return false;
}
if (!Modifier.isPublic(field.getModifiers())) {
return false;
}
final Class<?> clazz = field.getDeclaringClass();
if (!allow(clazz, false)) {
return false;
}
// is field annotated with nojexl ?
final NoJexl nojexl = field.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
return true;
}
/**
* Checks whether a method explicitly disallows JEXL introspection.
* <p>Since methods can be overridden, this also checks that no superclass or interface
* explicitly disallows this methods.</p>
* @param method the method to check
* @return true if JEXL is allowed to introspect, false otherwise
*/
public boolean allow(final Method method) {
if (method == null) {
return false;
}
if (!Modifier.isPublic(method.getModifiers())) {
return false;
}
// is method annotated with nojexl ?
NoJexl nojexl = method.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
// is the class annotated with nojexl ?
Class<?> clazz = method.getDeclaringClass();
nojexl = clazz.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
// lets walk all interfaces
for (final Class<?> inter : clazz.getInterfaces()) {
if (!allow(inter, method)) {
return false;
}
}
// lets walk all super classes
clazz = clazz.getSuperclass();
// walk all superclasses
while (clazz != null) {
if (!allow(clazz, method)) {
return false;
}
clazz = clazz.getSuperclass();
}
return true;
}
/**
* Checks whether a class or one of its superclasses or implemented interfaces
* explicitly disallows JEXL introspection.
* @param clazz the class to check
* @param interf whether interfaces should be checked as well
* @return true if JEXL is allowed to introspect, false otherwise
*/
protected boolean allow(Class<?> clazz, final boolean interf) {
if (clazz == null) {
return false;
}
if (!Modifier.isPublic(clazz.getModifiers())) {
return false;
}
// lets walk all interfaces
if (interf) {
for (final Class<?> inter : clazz.getInterfaces()) {
// is clazz annotated with nojexl ?
final NoJexl nojexl = inter.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
}
}
// lets walk all super classes
clazz = clazz.getSuperclass();
// walk all superclasses
while (clazz != null) {
// is clazz annotated with nojexl ?
final NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
clazz = clazz.getSuperclass();
}
return true;
}
/**
* Check whether a method is allowed to be JEXL introspected in all its
* superclasses and interfaces.
* @param clazz the class
* @param method the method
* @return true if JEXL is allowed to introspect, false otherwise
*/
protected boolean allow(final Class<?> clazz, final Method method) {
if (clazz != null) {
try {
// check if method in that class is different from the method argument
final Method wmethod = clazz.getMethod(method.getName(), method.getParameterTypes());
if (wmethod != null) {
NoJexl nojexl = clazz.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
// check if parent declaring class said nojexl (transitivity)
nojexl = wmethod.getAnnotation(NoJexl.class);
if (nojexl != null) {
return false;
}
}
} catch (final NoSuchMethodException ex) {
// unexpected, return no
return true;
} catch (final SecurityException ex) {
// unexpected, can't do much
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,117 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Specialized executor to get a property from an object.
* @since 2.0
*/
public final class PropertyGetExecutor extends AbstractExecutor.Get {
/** A static signature for method(). */
private static final Object[] EMPTY_PARAMS = {};
/** The property. */
private final String property;
/**
* Discovers a PropertyGetExecutor.
* <p>The method to be found should be named "get{P,p}property.</p>
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param property the property name to find
* @return the executor if found, null otherwise
*/
public static PropertyGetExecutor discover(final Introspector is, final Class<?> clazz, final String property) {
final java.lang.reflect.Method method = discoverGet(is, "get", clazz, property);
return method == null? null : new PropertyGetExecutor(clazz, method, property);
}
/**
* Creates an instance.
* @param clazz he class the get method applies to
* @param method the method held by this executor
* @param identifier the property to get
*/
private PropertyGetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final String identifier) {
super(clazz, method);
property = identifier;
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object o) throws IllegalAccessException, InvocationTargetException {
return method == null ? null : method.invoke(o, (Object[]) null);
}
@Override
public Object tryInvoke(final Object o, final Object identifier) {
if (o != null && method != null
&& property.equals(castString(identifier))
&& objectClass.equals(o.getClass())) {
try {
return method.invoke(o, (Object[]) null);
} catch (IllegalAccessException | IllegalArgumentException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return TRY_FAILED;
}
/**
* Base method for boolean and object property get.
* @param is the introspector
* @param which "is" or "get" for boolean or object
* @param clazz The class being examined.
* @param property The property being addressed.
* @return The {get,is}{p,P}roperty method if one exists, null otherwise.
*/
static java.lang.reflect.Method discoverGet(final Introspector is,
final String which,
final Class<?> clazz,
final String property) {
if (property == null || property.isEmpty()) {
return null;
}
// this is gross and linear, but it keeps it straightforward.
java.lang.reflect.Method method;
final int start = which.length(); // "get" or "is" so 3 or 2 for char case switch
// start with get<Property>
final StringBuilder sb = new StringBuilder(which);
sb.append(property);
// uppercase nth char
final char c = sb.charAt(start);
sb.setCharAt(start, Character.toUpperCase(c));
method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
//lowercase nth char
if (method == null) {
sb.setCharAt(start, Character.toLowerCase(c));
method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
}
return method;
}
}

View File

@ -0,0 +1,192 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
/**
* Specialized executor to set a property in an object.
* @since 2.0
*/
public class PropertySetExecutor extends AbstractExecutor.Set {
/** Index of the first character of the set{p,P}roperty. */
private static final int SET_START_INDEX = 3;
/** The property. */
protected final String property;
/** The property value class. */
protected final Class<?> valueClass;
/**
* Discovers a PropertySetExecutor.
* <p>The method to be found should be named "set{P,p}property.</p>
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param property the property name to find
* @param value the value to assign to the property
* @return the executor if found, null otherwise
*/
public static PropertySetExecutor discover(final Introspector is,
final Class<?> clazz,
final String property,
final Object value) {
if (property == null || property.isEmpty()) {
return null;
}
final java.lang.reflect.Method method = discoverSet(is, clazz, property, value);
return method != null? new PropertySetExecutor(clazz, method, property, value) : null;
}
/**
* Creates an instance.
* @param clazz the class the set method applies to
* @param method the method called through this executor
* @param key the key to use as 1st argument to the set method
* @param value the value
*/
protected PropertySetExecutor(final Class<?> clazz,
final java.lang.reflect.Method method,
final String key,
final Object value) {
super(clazz, method);
property = key;
valueClass = classOf(value);
}
@Override
public Object getTargetProperty() {
return property;
}
@Override
public Object invoke(final Object o, Object arg) throws IllegalAccessException, InvocationTargetException {
if (method != null) {
// handle the empty array case
if (isEmptyArray(arg)) {
// if array is empty but its component type is different from the method first parameter component type,
// replace argument with a new empty array instance (of the method first parameter component type)
final Class<?> componentType = method.getParameterTypes()[0].getComponentType();
if (componentType != null && !componentType.equals(arg.getClass().getComponentType())) {
arg = Array.newInstance(componentType, 0);
}
}
method.invoke(o, arg);
}
return arg;
}
@Override
public Object tryInvoke(final Object o, final Object identifier, final Object value) {
if (o != null && method != null
// ensure method name matches the property name
&& property.equals(castString(identifier))
// object class should be same as executor's method declaring class
&& objectClass.equals(o.getClass())
// argument class should be eq
&& valueClass.equals(classOf(value))) {
try {
return invoke(o, value);
} catch (IllegalAccessException | IllegalArgumentException xill) {
return TRY_FAILED;// fail
} catch (final InvocationTargetException xinvoke) {
throw JexlException.tryFailed(xinvoke); // throw
}
}
return TRY_FAILED;
}
/**
* Checks whether an argument is an empty array.
* @param arg the argument
* @return true if <code>arg</code> is an empty array
*/
private static boolean isEmptyArray(final Object arg) {
return (arg != null && arg.getClass().isArray() && Array.getLength(arg) == 0);
}
/**
* Discovers the method for a {@link JexlPropertySet}.
* <p>The method to be found should be named "set{P,p}property.
* As a special case, any empty array will try to find a valid array-setting non-ambiguous method.
*
* @param is the introspector
* @param clazz the class to find the get method from
* @param property the name of the property to set
* @param arg the value to assign to the property
* @return the method if found, null otherwise
*/
private static java.lang.reflect.Method discoverSet(final Introspector is,
final Class<?> clazz,
final String property,
final Object arg) {
// first, we introspect for the set<identifier> setter method
final Object[] params = {arg};
final StringBuilder sb = new StringBuilder("set");
sb.append(property);
// uppercase nth char
final char c = sb.charAt(SET_START_INDEX);
sb.setCharAt(SET_START_INDEX, Character.toUpperCase(c));
java.lang.reflect.Method method = is.getMethod(clazz, sb.toString(), params);
// lowercase nth char
if (method == null) {
sb.setCharAt(SET_START_INDEX, Character.toLowerCase(c));
method = is.getMethod(clazz, sb.toString(), params);
// uppercase nth char, try array
if (method == null && isEmptyArray(arg)) {
sb.setCharAt(SET_START_INDEX, Character.toUpperCase(c));
method = lookupSetEmptyArray(is, clazz, sb.toString());
// lowercase nth char
if (method == null) {
sb.setCharAt(SET_START_INDEX, Character.toLowerCase(c));
method = lookupSetEmptyArray(is, clazz, sb.toString());
}
}
}
return method;
}
/**
* Finds an empty array property setter method by <code>methodName</code>.
* <p>This checks only one method with that name accepts an array as sole parameter.
* @param is the introspector
* @param clazz the class to find the get method from
* @param mname the method name to find
* @return the sole method that accepts an array as parameter
*/
private static java.lang.reflect.Method lookupSetEmptyArray(final Introspector is, final Class<?> clazz, final String mname) {
java.lang.reflect.Method candidate = null;
final java.lang.reflect.Method[] methods = is.getMethods(clazz, mname);
if (methods != null) {
for (final java.lang.reflect.Method method : methods) {
final Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length == 1 && paramTypes[0].isArray()) {
if (candidate != null) {
// because the setter method is overloaded for different parameter type,
// return null here to report the ambiguity.
return null;
}
candidate = method;
}
}
}
return candidate;
}
}

View File

@ -0,0 +1,167 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlSandbox;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import java.util.Iterator;
import java.util.List;
/**
* An uberspect that controls usage of properties, methods and constructors through a sandbox.
* @since 3.0
*/
public final class SandboxUberspect implements JexlUberspect {
/** The base uberspect. */
private final JexlUberspect uberspect;
/** The sandbox. */
private final JexlSandbox sandbox;
/**
* A constructor for JexlSandbox uberspect.
* @param theUberspect the JexlUberspect to sandbox
* @param theSandbox the sandbox which is copied to avoid changes at runtime
*/
public SandboxUberspect(final JexlUberspect theUberspect, final JexlSandbox theSandbox) {
if (theSandbox == null) {
throw new NullPointerException("sandbox can not be null");
}
if (theUberspect == null) {
throw new NullPointerException("uberspect can not be null");
}
this.uberspect = theUberspect;
this.sandbox = theSandbox.copy();
}
@Override
public void setClassLoader(final ClassLoader loader) {
uberspect.setClassLoader(loader);
}
@Override
public ClassLoader getClassLoader() {
return uberspect.getClassLoader();
}
@Override
public int getVersion() {
return uberspect.getVersion();
}
@Override
public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
final String className;
if (ctorHandle instanceof Class<?>) {
className = sandbox.execute((Class<?>) ctorHandle, "");
} else if (ctorHandle != null) {
className = sandbox.execute(ctorHandle.toString(), "");
} else {
className = null;
}
return className != null && className != JexlSandbox.NULL ? uberspect.getConstructor(className, args) : null;
}
@Override
public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
if (obj != null && method != null) {
final Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
final String actual = sandbox.execute(clazz, method);
if (actual != null && actual != JexlSandbox.NULL) {
return uberspect.getMethod(obj, actual, args);
}
}
return null;
}
@Override
public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
return uberspect.getResolvers(op, obj);
}
@Override
public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
return getPropertyGet(null, obj, identifier);
}
@Override
public JexlPropertyGet getPropertyGet(final List<PropertyResolver> resolvers,
final Object obj,
final Object identifier) {
if (obj != null) {
if (identifier != null) {
final String property = identifier.toString();
final String actual = sandbox.read(obj.getClass(), property);
if (actual != null) {
// no transformation, strict equality: use identifier before string conversion
final Object pty = actual == property ? identifier : actual;
return uberspect.getPropertyGet(resolvers, obj, pty);
}
} else {
final String actual = sandbox.read(obj.getClass(), null);
if (actual != JexlSandbox.NULL) {
return uberspect.getPropertyGet(resolvers, obj, null);
}
}
}
return null;
}
@Override
public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
return getPropertySet(null, obj, identifier, arg);
}
@Override
public JexlPropertySet getPropertySet(final List<PropertyResolver> resolvers,
final Object obj,
final Object identifier,
final Object arg) {
if (obj != null) {
if (identifier != null) {
final String property = identifier.toString();
final String actual = sandbox.write(obj.getClass(), property);
if (actual != null) {
// no transformation, strict equality: use identifier before string conversion
final Object pty = actual == property ? identifier : actual;
return uberspect.getPropertySet(resolvers, obj, pty, arg);
}
} else {
final String actual = sandbox.write(obj.getClass(), null);
if (actual != JexlSandbox.NULL) {
return uberspect.getPropertySet(resolvers, obj, null, arg);
}
}
}
return null;
}
@Override
public Iterator<?> getIterator(final Object obj) {
return uberspect.getIterator(obj);
}
@Override
public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
return uberspect.getArithmetic(arithmetic);
}
}

View File

@ -0,0 +1,479 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.internal.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlMethod;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertyGet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlPropertySet;
import aiyh.utils.tool.org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.logging.Log;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implementation of Uberspect to provide the default introspective
* functionality of JEXL.
* <p>
* This is the class to derive to customize introspection.</p>
*
* @since 1.0
*/
public class Uberspect implements JexlUberspect {
/** Publicly exposed special failure object returned by tryInvoke. */
public static final Object TRY_FAILED = JexlEngine.TRY_FAILED;
/** The logger to use for all warnings and errors. */
protected final Log logger;
/** The resolver strategy. */
private final ResolverStrategy strategy;
/** The permissions. */
private final Permissions permissions;
/** The introspector version. */
private final AtomicInteger version;
/** The soft reference to the introspector currently in use. */
private volatile Reference<Introspector> ref;
/** The class loader reference; used to recreate the introspector when necessary. */
private volatile Reference<ClassLoader> loader;
/**
* The map from arithmetic classes to overloaded operator sets.
* <p>
* This keeps track of which operator methods are overloaded per JexlArithemtic classes
* allowing a fail fast test during interpretation by avoiding seeking a method when there is none.
*/
private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap;
/**
* Creates a new Uberspect.
*
* @param runtimeLogger the logger used for all logging needs
* @param sty the resolver strategy
*/
public Uberspect(final Log runtimeLogger, final ResolverStrategy sty) {
this(runtimeLogger, sty, null);
}
/**
* Creates a new Uberspect.
*
* @param runtimeLogger the logger used for all logging needs
* @param sty the resolver strategy
* @param perms the introspector permissions
*/
public Uberspect(final Log runtimeLogger, final ResolverStrategy sty, final Permissions perms) {
logger = runtimeLogger;
strategy = sty == null ? JexlUberspect.JEXL_STRATEGY : sty;
permissions = perms;
ref = new SoftReference<Introspector>(null);
loader = new SoftReference<ClassLoader>(getClass().getClassLoader());
operatorMap = new ConcurrentHashMap<Class<? extends JexlArithmetic>, Set<JexlOperator>>();
version = new AtomicInteger(0);
}
/**
* Gets the current introspector base.
* <p>
* If the reference has been collected, this method will recreate the underlying introspector.</p>
*
* @return the introspector
*/
// CSOFF: DoubleCheckedLocking
protected final Introspector base() {
Introspector intro = ref.get();
if (intro == null) {
// double checked locking is ok (fixed by Java 5 memory model).
synchronized (this) {
intro = ref.get();
if (intro == null) {
intro = new Introspector(logger, loader.get(), permissions);
ref = new SoftReference<Introspector>(intro);
loader = new SoftReference<ClassLoader>(intro.getLoader());
version.incrementAndGet();
}
}
}
return intro;
}
// CSON: DoubleCheckedLocking
@Override
public void setClassLoader(final ClassLoader nloader) {
synchronized (this) {
Introspector intro = ref.get();
if (intro != null) {
intro.setLoader(nloader);
} else {
intro = new Introspector(logger, nloader, permissions);
ref = new SoftReference<Introspector>(intro);
}
loader = new SoftReference<ClassLoader>(intro.getLoader());
operatorMap.clear();
version.incrementAndGet();
}
}
@Override
public ClassLoader getClassLoader() {
return loader.get();
}
@Override
public int getVersion() {
return version.intValue();
}
/**
* Gets a class by name through this introspector class loader.
*
* @param className the class name
* @return the class instance or null if it could not be found
*/
public final Class<?> getClassByName(final String className) {
return base().getClassByName(className);
}
/**
* Gets the field named by
* <code>key</code> for the class
* <code>c</code>.
*
* @param c Class in which the field search is taking place
* @param key Name of the field being searched for
* @return a {@link Field} or null if it does not exist or is not accessible
*/
public final Field getField(final Class<?> c, final String key) {
return base().getField(c, key);
}
/**
* Gets the accessible field names known for a given class.
*
* @param c the class
* @return the class field names
*/
public final String[] getFieldNames(final Class<?> c) {
return base().getFieldNames(c);
}
/**
* Gets the method defined by
* <code>name</code> and
* <code>params</code> for the Class
* <code>c</code>.
*
* @param c Class in which the method search is taking place
* @param name Name of the method being searched for
* @param params An array of Objects (not Classes) that describe the
* the parameters
* @return a {@link Method}
* or null if no unambiguous method could be found through introspection.
*/
public final Method getMethod(final Class<?> c, final String name, final Object[] params) {
return base().getMethod(c, new MethodKey(name, params));
}
/**
* Gets the method defined by
* <code>key</code> and for the Class
* <code>c</code>.
*
* @param c Class in which the method search is taking place
* @param key MethodKey of the method being searched for
* @return a {@link Method}
* or null if no unambiguous method could be found through introspection.
*/
public final Method getMethod(final Class<?> c, final MethodKey key) {
return base().getMethod(c, key);
}
/**
* Gets the accessible methods names known for a given class.
*
* @param c the class
* @return the class method names
*/
public final String[] getMethodNames(final Class<?> c) {
return base().getMethodNames(c);
}
/**
* Gets all the methods with a given name from this map.
*
* @param c the class
* @param methodName the seeked methods name
* @return the array of methods
*/
public final Method[] getMethods(final Class<?> c, final String methodName) {
return base().getMethods(c, methodName);
}
@Override
public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
return MethodExecutor.discover(base(), obj, method, args);
}
@Override
public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
return strategy.apply(op, obj);
}
@Override
public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
return getPropertyGet(null, obj, identifier);
}
@Override
public JexlPropertyGet getPropertyGet(
final List<PropertyResolver> resolvers, final Object obj, final Object identifier
) {
final Class<?> claz = obj.getClass();
final String property = AbstractExecutor.castString(identifier);
final Introspector is = base();
final List<PropertyResolver> r = resolvers == null ? strategy.apply(null, obj) : resolvers;
JexlPropertyGet executor = null;
for (final PropertyResolver resolver : r) {
if (resolver instanceof JexlResolver) {
switch ((JexlResolver) resolver) {
case PROPERTY:
// first try for a getFoo() type of property (also getfoo() )
executor = PropertyGetExecutor.discover(is, claz, property);
if (executor == null) {
executor = BooleanGetExecutor.discover(is, claz, property);
}
break;
case MAP:
// let's see if we are a map...
executor = MapGetExecutor.discover(is, claz, identifier);
break;
case LIST:
// let's see if this is a list or array
final Integer index = AbstractExecutor.castInteger(identifier);
if (index != null) {
executor = ListGetExecutor.discover(is, claz, index);
}
break;
case DUCK:
// if that didn't work, look for get(foo)
executor = DuckGetExecutor.discover(is, claz, identifier);
if (executor == null && property != null && property != identifier) {
// look for get("foo") if we did not try yet (just above)
executor = DuckGetExecutor.discover(is, claz, property);
}
break;
case FIELD:
// a field may be? (can not be a number)
executor = FieldGetExecutor.discover(is, claz, property);
// static class fields (enums included)
if (obj instanceof Class<?>) {
executor = FieldGetExecutor.discover(is, (Class<?>) obj, property);
}
break;
case CONTAINER:
// or an indexed property?
executor = IndexedType.discover(is, obj, property);
break;
default:
continue; // in case we add new ones in enum
}
} else {
executor = resolver.getPropertyGet(this, obj, identifier);
}
if (executor != null) {
return executor;
}
}
return null;
}
@Override
public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
return getPropertySet(null, obj, identifier, arg);
}
@Override
public JexlPropertySet getPropertySet(
final List<PropertyResolver> resolvers, final Object obj, final Object identifier, final Object arg
) {
final Class<?> claz = obj.getClass();
final String property = AbstractExecutor.castString(identifier);
final Introspector is = base();
final List<PropertyResolver> actual = resolvers == null ? strategy.apply(null, obj) : resolvers;
JexlPropertySet executor = null;
for (final PropertyResolver resolver : actual) {
if (resolver instanceof JexlResolver) {
switch ((JexlResolver) resolver) {
case PROPERTY:
// first try for a setFoo() type of property (also setfoo() )
executor = PropertySetExecutor.discover(is, claz, property, arg);
break;
case MAP:
// let's see if we are a map...
executor = MapSetExecutor.discover(is, claz, identifier, arg);
break;
case LIST:
// let's see if we can convert the identifier to an int,
// if obj is an array or a list, we can still do something
final Integer index = AbstractExecutor.castInteger(identifier);
if (index != null) {
executor = ListSetExecutor.discover(is, claz, identifier, arg);
}
break;
case DUCK:
// if that didn't work, look for set(foo)
executor = DuckSetExecutor.discover(is, claz, identifier, arg);
if (executor == null && property != null && property != identifier) {
executor = DuckSetExecutor.discover(is, claz, property, arg);
}
break;
case FIELD:
// a field may be?
executor = FieldSetExecutor.discover(is, claz, property, arg);
break;
case CONTAINER:
default:
continue; // in case we add new ones in enum
}
} else {
executor = resolver.getPropertySet(this, obj, identifier, arg);
}
if (executor != null) {
return executor;
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public Iterator<?> getIterator(final Object obj) {
if (obj instanceof Iterator<?>) {
return ((Iterator<?>) obj);
}
if (obj.getClass().isArray()) {
return new ArrayIterator(obj);
}
if (obj instanceof Map<?, ?>) {
return ((Map<?, ?>) obj).values().iterator();
}
if (obj instanceof Enumeration<?>) {
return new EnumerationIterator<Object>((Enumeration<Object>) obj);
}
if (obj instanceof Iterable<?>) {
return ((Iterable<?>) obj).iterator();
}
try {
// look for an iterator() method to support the JDK5 Iterable
// interface or any user tools/DTOs that want to work in
// foreach without implementing the Collection interface
final JexlMethod it = getMethod(obj, "iterator", (Object[]) null);
if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
return (Iterator<Object>) it.invoke(obj, (Object[]) null);
}
} catch (final Exception xany) {
if (logger != null && logger.isDebugEnabled()) {
logger.info("unable to solve iterator()", xany);
}
}
return null;
}
@Override
public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
return ConstructorMethod.discover(base(), ctorHandle, args);
}
/**
* The concrete uberspect Arithmetic class.
*/
protected class ArithmeticUberspect implements JexlArithmetic.Uberspect {
/** The arithmetic instance being analyzed. */
private final JexlArithmetic arithmetic;
/** The set of overloaded operators. */
private final Set<JexlOperator> overloads;
/**
* Creates an instance.
*
* @param theArithmetic the arithmetic instance
* @param theOverloads the overloaded operators
*/
private ArithmeticUberspect(final JexlArithmetic theArithmetic, final Set<JexlOperator> theOverloads) {
this.arithmetic = theArithmetic;
this.overloads = theOverloads;
}
@Override
public JexlMethod getOperator(final JexlOperator operator, final Object... args) {
return overloads.contains(operator) && args != null
? getMethod(arithmetic, operator.getMethodName(), args)
: null;
}
@Override
public boolean overloads(final JexlOperator operator) {
return overloads.contains(operator);
}
}
@Override
public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
JexlArithmetic.Uberspect jau = null;
if (arithmetic != null) {
final Class<? extends JexlArithmetic> aclass = arithmetic.getClass();
Set<JexlOperator> ops = operatorMap.get(aclass);
if (ops == null) {
ops = EnumSet.noneOf(JexlOperator.class);
// deal only with derived classes
if (!JexlArithmetic.class.equals(aclass)) {
for (final JexlOperator op : JexlOperator.values()) {
final Method[] methods = getMethods(arithmetic.getClass(), op.getMethodName());
if (methods != null) {
for (final Method method : methods) {
final Class<?>[] parms = method.getParameterTypes();
if (parms.length != op.getArity()) {
continue;
}
// filter method that is an actual overload:
// - not inherited (not declared by base class)
// - nor overridden (not present in base class)
if (!JexlArithmetic.class.equals(method.getDeclaringClass())) {
try {
JexlArithmetic.class.getMethod(method.getName(), method.getParameterTypes());
} catch (final NoSuchMethodException xmethod) {
// method was not found in JexlArithmetic; this is an operator definition
ops.add(op);
}
}
}
}
}
}
// register this arithmetic class in the operator map
operatorMap.put(aclass, ops);
}
jau = new ArithmeticUberspect(arithmetic, ops);
}
return jau;
}
}

View File

@ -0,0 +1,39 @@
<html>
<!--
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.
-->
<head>
<title>Package Documentation for org.apache.commons.jexl3.introspection Package</title>
</head>
<body>
Provides low-level introspective services.
<p>
This internal package is not intended for public usage and there is <b>no</b>
guarantee that its public classes or methods will remain as is in subsequent
versions.
</p>
<p>
The IntrospectorBase, ClassMap, MethodKey, MethodMap form the
base of the introspection service. They allow to describe classes and their
methods, keeping them in a cache (@see IntrospectorBase) to speed up property
getters/setters and method discovery used during expression evaluation.
</p>
<p>
The cache materialized in Introspector creates one entry per class containing a map of all
accessible public methods keyed by name and signature.
</p>
</body>
</html>

View File

@ -0,0 +1,37 @@
<html>
<!--
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.
-->
<head>
<title>Package Documentation for org.apache.commons.jexl3 Package</title>
</head>
<body>
<h2>Provides utilities for introspection services.</h2>
<p>
This internal package is not intended for public usage and there is <b>no</b>
guarantee that its public classes or methods will remain as is in subsequent
versions.
</p>
<p>
This set of classes implement the various forms of setters and getters
used by JEXL. These are specialized forms for 'pure' properties, discovering
methods of the {s,g}etProperty form, for Maps, Lists and Ducks -
attempting to discover a 'get' or 'set' method, making an object walk and
quack.
</p>
</body>
</html>

View File

@ -0,0 +1,80 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Interface used for regular method invocation.
* Ex.
* <code>
* ${foo.bar()}
* </code>
*
* @since 1.0
*/
public interface JexlMethod {
/**
* Invocation method, called when the method invocation should be performed
* and a value returned.
* @param obj the object
* @param params method parameters.
* @return the result
* @throws Exception on any error.
*/
Object invoke(Object obj, Object... params) throws Exception;
/**
* Attempts to reuse this JexlMethod, checking that it is compatible with
* the actual set of arguments.
* Related to isCacheable since this method is often used with cached JexlMethod instances.
*
* @param name the method name
* @param obj the object to invoke the method upon
* @param params the method arguments
* @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
* or failed.
* @throws JexlException.TryFailed if the underlying method was invoked but threw an exception
* ({@link java.lang.reflect.InvocationTargetException})
*/
Object tryInvoke(String name, Object obj, Object... params) throws JexlException.TryFailed;
/**
* Checks whether a tryInvoke return value indicates a failure or not.
* <p>Usage is : <code>Object r = tryInvoke(...); if (tryFailed(r) {...} else {...}</code>
*
* @param rval the value returned by tryInvoke
* @return true if tryInvoke failed, false otherwise
*/
boolean tryFailed(Object rval);
/**
* Specifies if this JexlMethod is cacheable and able to be reused for this
* class of object it was returned for.
*
* @return true if can be reused for this class, false if not
*/
boolean isCacheable();
/**
* returns the return type of the method invoked.
*
* @return return type
*/
Class<?> getReturnType();
}

View File

@ -0,0 +1,69 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Interface for getting values that appear to be properties.
* Ex.
* <code>
* ${foo.bar}
* </code>
*
* @since 1.0
*/
public interface JexlPropertyGet {
/**
* Method used to get the property value of an object.
*
* @param obj the object to get the property value from.
* @return the property value.
* @throws Exception on any error.
*/
Object invoke(Object obj) throws Exception;
/**
* Attempts to reuse this JexlPropertyGet, checking that it is compatible with
* the actual set of arguments.
*
* @param obj the object to invoke the property get upon
* @param key the property key to get
* @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
* or failed.
* @throws JexlException.TryFailed if the underlying method was invoked but threw an exception
* ({@link java.lang.reflect.InvocationTargetException})
*/
Object tryInvoke(Object obj, Object key) throws JexlException.TryFailed;
/**
* Checks whether a tryInvoke failed or not.
*
* @param rval the value returned by tryInvoke
* @return true if tryInvoke failed, false otherwise
*/
boolean tryFailed(Object rval);
/**
* Specifies if this JexlPropertyGet is cacheable and able to be reused for
* this class of object it was returned for.
*
* @return true if can be reused for this class, false if not
*/
boolean isCacheable();
}

View File

@ -0,0 +1,71 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlException;
/**
* Interface used for setting values that appear to be properties.
* Ex.
* <code>
* ${foo.bar = "hello"}
* </code>
*
* @since 1.0
*/
public interface JexlPropertySet {
/**
* Method used to set the property value of an object.
*
* @param obj Object on which the property setter will be called with the value
* @param arg value to be set
* @return the value returned from the set operation (impl specific)
* @throws Exception on any error.
*/
Object invoke(Object obj, Object arg) throws Exception;
/**
* Attempts to reuse this JexlPropertySet, checking that it is compatible with
* the actual set of arguments.
*
* @param obj the object to invoke the get upon
* @param key the property key to get
* @param value the property value to set
* @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
* or failed.
* @throws JexlException.TryFailed if the underlying method was invoked but threw an exception
* ({@link java.lang.reflect.InvocationTargetException})
*/
Object tryInvoke(Object obj, Object key, Object value) throws JexlException.TryFailed;
/**
* Checks whether a tryInvoke failed or not.
*
* @param rval the value returned by tryInvoke
* @return true if tryInvoke failed, false otherwise
*/
boolean tryFailed(Object rval);
/**
* Specifies if this JexlPropertySet is cacheable and able to be reused for
* this class of object it was returned for.
*
* @return true if can be reused for this class, false if not
*/
boolean isCacheable();
}

View File

@ -0,0 +1,681 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.introspection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A sandbox describes permissions on a class by explicitly allowing or forbidding
* access to methods and properties through "allowlists" and "blocklists".
*
* <p>A <b>allowlist</b> explicitly allows methods/properties for a class;</p>
*
* <ul>
* <li>If a allowlist is empty and thus does not contain any names,
* all properties/methods are allowed for its class.</li>
* <li>If it is not empty, the only allowed properties/methods are the ones contained.</li>
* </ul>
*
* <p>A <b>blocklist</b> explicitly forbids methods/properties for a class;</p>
*
* <ul>
* <li>If a blocklist is empty and thus does not contain any names,
* all properties/methods are forbidden for its class.</li>
* <li>If it is not empty, the only forbidden properties/methods are the ones contained.</li>
* </ul>
*
* <p>Permissions are composed of three lists, read, write, execute, each being
* "allow" or "block":</p>
*
* <ul>
* <li><b>read</b> controls readable properties </li>
* <li><b>write</b> controls writable properties</li>
* <li><b>execute</b> controls executable methods and constructor</li>
* </ul>
*
* <p>When specified, permissions - allow or block lists - can be created inheritable
* on interfaces or classes and thus applicable to their implementations or derived
* classes; the sandbox must be created with the 'inheritable' flag for this behavior
* to be triggered. Note that even in this configuration, it is still possible to
* add non-inheritable permissions.
* Adding inheritable lists to a non inheritable sandbox has no added effect;
* permissions only apply to their specified class.</p>
*
* <p>Note that a JexlUberspect always uses a <em>copy</em> of the JexlSandbox
* used to built it preventing permission changes after its instantiation.</p>
*
* @since 3.0
*/
public final class JexlSandbox {
/**
* The marker string for explicitly disallowed null properties.
*/
public static final String NULL = "?";
/**
* The map from class names to permissions.
*/
private final Map<String, Permissions> sandbox;
/**
* Whether permissions can be inherited (through implementation or extension).
*/
private final boolean inherit;
/**
* Default behavior, block or allow.
*/
private final boolean allow;
/**
* Creates a new default sandbox.
* <p>In the absence of explicit permissions on a class, the
* sandbox is a allow-box, allow-listing that class for all permissions (read, write and execute).
*/
public JexlSandbox() {
this(true, false, null);
}
/**
* Creates a new default sandbox.
* <p>A allow-box considers no permissions as &quot;everything is allowed&quot; when
* a block-box considers no permissions as &quot;nothing is allowed&quot;.
* @param ab whether this sandbox is allow (true) or block (false)
* if no permission is explicitly defined for a class.
* @since 3.1
*/
public JexlSandbox(final boolean ab) {
this(ab, false, null);
}
/**
* Creates a sandbox.
* @param ab whether this sandbox is allow (true) or block (false)
* @param inh whether permissions on interfaces and classes are inherited (true) or not (false)
* @since 3.2
*/
public JexlSandbox(final boolean ab, final boolean inh) {
this(ab, inh, null);
}
/**
* Creates a sandbox based on an existing permissions map.
* @param map the permissions map
*/
@Deprecated
protected JexlSandbox(final Map<String, Permissions> map) {
this(true, false, map);
}
/**
* Creates a sandbox based on an existing permissions map.
* @param ab whether this sandbox is allow (true) or block (false)
* @param map the permissions map
* @since 3.1
*/
@Deprecated
protected JexlSandbox(final boolean ab, final Map<String, Permissions> map) {
this(ab, false, map);
}
/**
* Creates a sandbox based on an existing permissions map.
* @param ab whether this sandbox is allow (true) or block (false)
* @param inh whether permissions are inherited, default false
* @param map the permissions map
* @since 3.2
*/
protected JexlSandbox(final boolean ab, final boolean inh, final Map<String, Permissions> map) {
allow = ab;
inherit = inh;
sandbox = map != null? map : new HashMap<>();
}
/**
* @return a copy of this sandbox
*/
public JexlSandbox copy() {
// modified concurrently at runtime so...
final Map<String, Permissions> map = new ConcurrentHashMap<>();
for (final Map.Entry<String, Permissions> entry : sandbox.entrySet()) {
map.put(entry.getKey(), entry.getValue().copy());
}
return new JexlSandbox(allow, inherit, map);
}
/**
* Gets a class by name, crude mechanism for backwards (&lt;3.2 ) compatibility.
* @param cname the class name
* @return the class
*/
static Class<?> forName(final String cname) {
try {
return Class.forName(cname);
} catch(final Exception xany) {
return null;
}
}
/**
* Gets the read permission value for a given property of a class.
*
* @param clazz the class
* @param name the property name
* @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise
*/
public String read(final Class<?> clazz, final String name) {
return get(clazz).read().get(name);
}
/**
* Gets the read permission value for a given property of a class.
*
* @param clazz the class name
* @param name the property name
* @return null if not allowed, the name of the property to use otherwise
*/
@Deprecated
public String read(final String clazz, final String name) {
return get(clazz).read().get(name);
}
/**
* Gets the write permission value for a given property of a class.
*
* @param clazz the class
* @param name the property name
* @return null (or NULL if name is null) if not allowed, the name of the property to use otherwise
*/
public String write(final Class<?> clazz, final String name) {
return get(clazz).write().get(name);
}
/**
* Gets the write permission value for a given property of a class.
*
* @param clazz the class name
* @param name the property name
* @return null if not allowed, the name of the property to use otherwise
*/
@Deprecated
public String write(final String clazz, final String name) {
return get(clazz).write().get(name);
}
/**
* Gets the execute permission value for a given method of a class.
*
* @param clazz the class
* @param name the method name
* @return null if not allowed, the name of the method to use otherwise
*/
public String execute(final Class<?> clazz, final String name) {
final String m = get(clazz).execute().get(name);
return "".equals(name) && m != null? clazz.getName() : m;
}
/**
* Gets the execute permission value for a given method of a class.
*
* @param clazz the class name
* @param name the method name
* @return null if not allowed, the name of the method to use otherwise
*/
@Deprecated
public String execute(final String clazz, final String name) {
final String m = get(clazz).execute().get(name);
return "".equals(name) && m != null? clazz : m;
}
/**
* A base set of names.
*/
public abstract static class Names {
/**
* Adds a name to this set.
*
* @param name the name to add
* @return true if the name was really added, false if not
*/
public abstract boolean add(String name);
/**
* Adds an alias to a name to this set.
* <p>This only has an effect on allow lists.</p>
*
* @param name the name to alias
* @param alias the alias
* @return true if the alias was added, false if it was already present
*/
public boolean alias(final String name, final String alias) {
return false;
}
/**
* Whether a given name is allowed or not.
*
* @param name the method/property name to check
* @return null (or NULL if name is null) if not allowed, the actual name to use otherwise
*/
public String get(final String name) {
return name;
}
/**
* @return a copy of these Names
*/
protected Names copy() {
return this;
}
}
/**
* The pass-thru name set.
*/
private static final Names ALLOW_NAMES = new Names() {
@Override
public boolean add(final String name) {
return false;
}
@Override
protected Names copy() {
return this;
}
};
/**
* The block-all name set.
*/
private static final Names BLOCK_NAMES = new Names() {
@Override
public boolean add(final String name) {
return false;
}
@Override
protected Names copy() {
return this;
}
@Override
public String get(final String name) {
return name == null? NULL : null;
}
};
/**
* A allow set of names.
*/
static class AllowSet extends Names {
/** The map of controlled names and aliases. */
private Map<String, String> names = null;
@Override
protected Names copy() {
final AllowSet copy = new AllowSet();
copy.names = names == null ? null : new HashMap<>(names);
return copy;
}
@Override
public boolean add(final String name) {
if (names == null) {
names = new HashMap<>();
}
return names.put(name, name) == null;
}
@Override
public boolean alias(final String name, final String alias) {
if (names == null) {
names = new HashMap<>();
}
return names.put(alias, name) == null;
}
@Override
public String get(final String name) {
if (names == null) {
return name;
}
String actual = names.get(name);
// if null is not explicitly allowed, explicit null aka NULL
if (name == null && actual == null && !names.containsKey(null)) {
return JexlSandbox.NULL;
}
return actual;
}
}
/**
* A block set of names.
*/
static class BlockSet extends Names {
/** The set of controlled names. */
private Set<String> names = null;
@Override
protected Names copy() {
final BlockSet copy = new BlockSet();
copy.names = names == null ? null : new HashSet<>(names);
return copy;
}
@Override
public boolean add(final String name) {
if (names == null) {
names = new HashSet<>();
}
return names.add(name);
}
@Override
public String get(final String name) {
// if name is null and contained in set, explicit null aka NULL
return names != null && !names.contains(name) ? name : name != null? null : NULL;
}
}
/**
* Unused.
*/
@Deprecated
public static final class WhiteSet extends AllowSet {}
/**
* Unused.
*/
@Deprecated
public static final class BlackSet extends BlockSet {}
/**
* Contains the allow or block lists for properties and methods for a given class.
*/
public static final class Permissions {
/** Whether these permissions are inheritable, ie can be used by derived classes. */
private final boolean inheritable;
/** The controlled readable properties. */
private final Names read;
/** The controlled writable properties. */
private final Names write;
/** The controlled methods. */
private final Names execute;
/**
* Creates a new permissions instance.
*
* @param inherit whether these permissions are inheritable
* @param readFlag whether the read property list is allow or block
* @param writeFlag whether the write property list is allow or block
* @param executeFlag whether the method list is allow of block
*/
Permissions(final boolean inherit, final boolean readFlag, final boolean writeFlag, final boolean executeFlag) {
this(inherit,
readFlag ? new AllowSet() : new BlockSet(),
writeFlag ? new AllowSet() : new BlockSet(),
executeFlag ? new AllowSet() : new BlockSet());
}
/**
* Creates a new permissions instance.
*
* @param inherit whether these permissions are inheritable
* @param nread the read set
* @param nwrite the write set
* @param nexecute the method set
*/
Permissions(final boolean inherit, final Names nread, final Names nwrite, final Names nexecute) {
this.read = nread != null ? nread : ALLOW_NAMES;
this.write = nwrite != null ? nwrite : ALLOW_NAMES;
this.execute = nexecute != null ? nexecute : ALLOW_NAMES;
this.inheritable = inherit;
}
/**
* @return a copy of these permissions
*/
Permissions copy() {
return new Permissions(inheritable, read.copy(), write.copy(), execute.copy());
}
/**
* @return whether these permissions applies to derived classes.
*/
public boolean isInheritable() {
return inheritable;
}
/**
* Adds a list of readable property names to these permissions.
*
* @param pnames the property names
* @return this instance of permissions
*/
public Permissions read(final String... pnames) {
for (final String pname : pnames) {
read.add(pname);
}
return this;
}
/**
* Adds a list of writable property names to these permissions.
*
* @param pnames the property names
* @return this instance of permissions
*/
public Permissions write(final String... pnames) {
for (final String pname : pnames) {
write.add(pname);
}
return this;
}
/**
* Adds a list of executable methods names to these permissions.
* <p>The constructor is denoted as the empty-string, all other methods by their names.</p>
*
* @param mnames the method names
* @return this instance of permissions
*/
public Permissions execute(final String... mnames) {
for (final String mname : mnames) {
execute.add(mname);
}
return this;
}
/**
* Gets the set of readable property names in these permissions.
*
* @return the set of property names
*/
public Names read() {
return read;
}
/**
* Gets the set of writable property names in these permissions.
*
* @return the set of property names
*/
public Names write() {
return write;
}
/**
* Gets the set of method names in these permissions.
*
* @return the set of method names
*/
public Names execute() {
return execute;
}
}
/**
* The pass-thru permissions.
*/
private static final Permissions ALLOW_ALL = new Permissions(false, ALLOW_NAMES, ALLOW_NAMES, ALLOW_NAMES);
/**
* The block-all permissions.
*/
private static final Permissions BLOCK_ALL = new Permissions(false, BLOCK_NAMES, BLOCK_NAMES, BLOCK_NAMES);
/**
* Creates the set of permissions for a given class.
* <p>The sandbox inheritance property will apply to the permissions created by this method
*
* @param clazz the class for which these permissions apply
* @param readFlag whether the readable property list is allow - true - or block - false -
* @param writeFlag whether the writable property list is allow - true - or block - false -
* @param executeFlag whether the executable method list is allow allow - true - or block - false -
* @return the set of permissions
*/
public Permissions permissions(final String clazz,
final boolean readFlag,
final boolean writeFlag,
final boolean executeFlag) {
return permissions(clazz, inherit, readFlag, writeFlag, executeFlag);
}
/**
* Creates the set of permissions for a given class.
*
* @param clazz the class for which these permissions apply
* @param inhf whether these permissions are inheritable
* @param readf whether the readable property list is allow - true - or block - false -
* @param writef whether the writable property list is allow - true - or block - false -
* @param execf whether the executable method list is allow allow - true - or block - false -
* @return the set of permissions
*/
public Permissions permissions(final String clazz,
final boolean inhf,
final boolean readf,
final boolean writef,
final boolean execf) {
final Permissions box = new Permissions(inhf, readf, writef, execf);
sandbox.put(clazz, box);
return box;
}
/**
* Creates a new set of permissions based on allow lists for methods and properties for a given class.
* <p>The sandbox inheritance property will apply to the permissions created by this method
*
* @param clazz the allowed class name
* @return the permissions instance
*/
public Permissions allow(final String clazz) {
return permissions(clazz, true, true, true);
}
/**
* Use allow() instead.
* @param clazz the allowed class name
* @return the permissions instance
*/
@Deprecated
public Permissions white(final String clazz) {
return allow(clazz);
}
/**
* Creates a new set of permissions based on block lists for methods and properties for a given class.
* <p>The sandbox inheritance property will apply to the permissions created by this method
*
* @param clazz the blocked class name
* @return the permissions instance
*/
public Permissions block(final String clazz) {
return permissions(clazz, false, false, false);
}
/**
* Use block() instead.
* @param clazz the allowed class name
* @return the permissions instance
*/
@Deprecated
public Permissions black(final String clazz) {
return block(clazz);
}
/**
* Gets the set of permissions associated to a class.
*
* @param clazz the class name
* @return the defined permissions or an all-allow permission instance if none were defined
*/
public Permissions get(final String clazz) {
if (inherit) {
return get(forName(clazz));
}
final Permissions permissions = sandbox.get(clazz);
if (permissions == null) {
return allow ? ALLOW_ALL : BLOCK_ALL;
}
return permissions;
}
/**
* Get the permissions associated to a class.
* @param clazz the class
* @return the permissions
*/
@SuppressWarnings("null") // clazz can not be null since permissions would be not null and block;
public Permissions get(final Class<?> clazz) {
Permissions permissions = clazz == null ? BLOCK_ALL : sandbox.get(clazz.getName());
if (permissions == null) {
if (inherit) {
// find first inherited interface that defines permissions
for (final Class<?> inter : clazz.getInterfaces()) {
permissions = sandbox.get(inter.getName());
if (permissions != null && permissions.isInheritable()) {
break;
}
}
// nothing defined yet, find first superclass that defines permissions
if (permissions == null) {
// lets walk all super classes
Class<?> zuper = clazz.getSuperclass();
// walk all superclasses
while (zuper != null) {
permissions = sandbox.get(zuper.getName());
if (permissions != null && permissions.isInheritable()) {
break;
}
zuper = zuper.getSuperclass();
}
}
// nothing was inheritable
if (permissions == null) {
permissions = allow ? ALLOW_ALL : BLOCK_ALL;
}
// store the info to avoid doing this costly look up
sandbox.put(clazz.getName(), permissions);
} else {
permissions = allow ? ALLOW_ALL : BLOCK_ALL;
}
}
return permissions;
}
}

View File

@ -0,0 +1,318 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.introspection;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlArithmetic;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlBuilder;
import aiyh.utils.tool.org.apache.commons.jexl3.JexlOperator;
import java.util.*;
/**
* 'Federated' introspection/reflection interface to allow JEXL introspection
* behavior to be customized.
*
* @since 1.0
*/
public interface JexlUberspect {
/**
* Abstracts getting property setter and getter.
* <p>
* These are used through 'strategies' to solve properties; a strategy orders a list of resolver types,
* and each resolver type is tried in sequence; the first resolver that discovers a non null {s,g}etter
* stops the search.
*
* @see JexlResolver
* @see JexlUberspect#getPropertyGet
* @see JexlUberspect#getPropertySet
* @since 3.0
*/
interface PropertyResolver {
/**
* Gets a property getter.
*
* @param uber the uberspect
* @param obj the object
* @param identifier the property identifier
* @return the property getter or null
*/
JexlPropertyGet getPropertyGet(JexlUberspect uber, Object obj, Object identifier);
/**
* Gets a property setter.
*
* @param uber the uberspect
* @param obj the object
* @param identifier the property identifier
* @param arg the property value
* @return the property setter or null
*/
JexlPropertySet getPropertySet(JexlUberspect uber, Object obj, Object identifier, Object arg);
}
/**
* The various builtin property resolvers.
* <p>
* Each resolver discovers how to set/get a property with different techniques; seeking
* method names or field names, etc.
*
* @since 3.0
*/
enum JexlResolver implements PropertyResolver {
/** Seeks methods named get{P,p}property and is{P,p}property. */
PROPERTY,
/** Seeks map methods get/put. */
MAP,
/** Seeks list methods get/set. */
LIST,
/** Seeks any get/{set,put} method (quacking like a list or a map). */
DUCK,
/** Seeks public instance members. */
FIELD,
/** Seeks a getContainer(property) and setContainer(property, value) as in <code>x.container.property</code>. */
CONTAINER;
@Override
public final JexlPropertyGet getPropertyGet(final JexlUberspect uber,
final Object obj,
final Object identifier) {
return uber.getPropertyGet(Collections.singletonList(this), obj, identifier);
}
@Override
public final JexlPropertySet getPropertySet(final JexlUberspect uber,
final Object obj,
final Object identifier,
final Object arg) {
return uber.getPropertySet(Collections.singletonList(this), obj, identifier, arg);
}
}
/**
* A resolver types list tailored for POJOs, favors '.' over '[]'.
*/
List<PropertyResolver> POJO = Collections.unmodifiableList(Arrays.asList(
JexlResolver.PROPERTY,
JexlResolver.MAP,
JexlResolver.LIST,
JexlResolver.DUCK,
JexlResolver.FIELD,
JexlResolver.CONTAINER
));
/**
* A resolver types list tailored for Maps, favors '[]' over '.'.
*/
List<PropertyResolver> MAP = Collections.unmodifiableList(Arrays.asList(
JexlResolver.MAP,
JexlResolver.LIST,
JexlResolver.DUCK,
JexlResolver.PROPERTY,
JexlResolver.FIELD,
JexlResolver.CONTAINER
));
/**
* Determines property resolution strategy.
*
* <p>To use a strategy instance, you have to set it at engine creation using
* {@link JexlBuilder#strategy(ResolverStrategy)}
* as in:</p>
*
* <code>JexlEngine jexl = new JexlBuilder().strategy(MY_STRATEGY).create();</code>
*
* @since 3.0
*/
interface ResolverStrategy {
/**
* Applies this strategy to a list of resolver types.
*
* @param operator the property access operator, may be null
* @param obj the instance we seek to obtain a property setter/getter from, can not be null
* @return the ordered list of resolvers types, must not be null
*/
List<PropertyResolver> apply(JexlOperator operator, Object obj);
}
/**
* The default strategy.
* <p>
* If the operator is '[]' or if the operator is null and the object is a map, use the MAP list of resolvers;
* Other cases use the POJO list of resolvers.
*/
ResolverStrategy JEXL_STRATEGY = (op, obj) -> {
if (op == JexlOperator.ARRAY_GET) {
return MAP;
}
if (op == JexlOperator.ARRAY_SET) {
return MAP;
}
if (op == null && obj instanceof Map) {
return MAP;
}
return POJO;
};
/**
* The map strategy.
*
* <p>If the operator is '[]' or if the object is a map, use the MAP list of resolvers.
* Otherwise, use the POJO list of resolvers.</p>
*/
ResolverStrategy MAP_STRATEGY = (op, obj) -> {
if (op == JexlOperator.ARRAY_GET) {
return MAP;
}
if (op == JexlOperator.ARRAY_SET) {
return MAP;
}
if (obj instanceof Map) {
return MAP;
}
return POJO;
};
/**
* Applies this uberspect property resolver strategy.
*
* @param op the operator
* @param obj the object
* @return the applied strategy resolver list
*/
List<PropertyResolver> getResolvers(JexlOperator op, Object obj);
/**
* Sets the class loader to use.
*
* <p>This increments the version.</p>
*
* @param loader the class loader
*/
void setClassLoader(ClassLoader loader);
/**
* Gets the current class loader.
*
* @return the class loader
*/
ClassLoader getClassLoader();
/**
* Gets this uberspect version.
*
* @return the class loader modification count
*/
int getVersion();
/**
* Returns a class constructor.
*
* @param ctorHandle a class or class name
* @param args constructor arguments
* @return a {@link JexlMethod}
* @since 3.0
*/
JexlMethod getConstructor(Object ctorHandle, Object... args);
/**
* Returns a JexlMethod.
*
* @param obj the object
* @param method the method name
* @param args method arguments
* @return a {@link JexlMethod}
*/
JexlMethod getMethod(Object obj, String method, Object... args);
/**
* Property getter.
*
* <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p>
*
* @param obj the object to get the property from
* @param identifier property name
* @return a {@link JexlPropertyGet} or null
*/
JexlPropertyGet getPropertyGet(Object obj, Object identifier);
/**
* Property getter.
* <p>
* Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p>
* See {@link ResolverStrategy#apply(JexlOperator, Object) }
*
* @param resolvers the list of property resolvers to try
* @param obj the object to get the property from
* @param identifier property name
* @return a {@link JexlPropertyGet} or null
* @since 3.0
*/
JexlPropertyGet getPropertyGet(List<PropertyResolver> resolvers, Object obj, Object identifier);
/**
* Property setter.
* <p>
* Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
*
* @param obj the object to get the property from.
* @param identifier property name
* @param arg value to set
* @return a {@link JexlPropertySet} or null
*/
JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg);
/**
* Property setter.
* <p>
* Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p>
* See {@link ResolverStrategy#apply(JexlOperator, Object) }
*
* @param resolvers the list of property resolvers to try,
* @param obj the object to get the property from
* @param identifier property name
* @param arg value to set
* @return a {@link JexlPropertySet} or null
* @since 3.0
*/
JexlPropertySet getPropertySet(List<PropertyResolver> resolvers, Object obj, Object identifier, Object arg);
/**
* Gets an iterator from an object.
*
* @param obj to get the iterator from
* @return an iterator over obj or null
*/
Iterator<?> getIterator(Object obj);
/**
* Gets an arithmetic operator resolver for a given arithmetic instance.
*
* @param arithmetic the arithmetic instance
* @return the arithmetic uberspect or null if no operator method were overridden
* @since 3.0
*/
JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic);
}

View File

@ -0,0 +1,34 @@
<html>
<!--
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.
-->
<head>
<title>Package Documentation for org.apache.commons.jexl3.introspection Package</title>
</head>
<body>
<h2>Provides high-level introspective services.</h2>
<p>
The Uberspect, JexlMethod, JexlPropertyGet and JexlPropertySet interfaces
form the exposed face of introspective services.
</p>
<p>
The Uberspectimpl is the concrete class implementing the Uberspect interface.
Deriving from this class is the preferred way of augmenting Jexl introspective
capabilities when special needs to be fulfilled or when default behaviors
need to be modified.
</p>
</body>
</html>

View File

@ -0,0 +1,535 @@
<html>
<!--
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.
-->
<head>
<title>Package Documentation for org.apache.commons.jexl3 Package</title>
</head>
<body>
Provides a framework for evaluating JEXL expressions.
<ul>
<li><a href="#intro">Introduction</a></li>
<li><a href="#example">Brief Example</a></li>
<li><a href="#usage">Using JEXL</a></li>
<li><a href="#configuration">Configuring JEXL</a></li>
<li><a href="#customization">Customizing JEXL</a></li>
<li><a href="#extension">Extending JEXL</a></li>
</ul>
<h2><a id="intro">Introduction</a></h2>
<p>
JEXL is a library intended to facilitate the implementation of dynamic and scripting features in applications
and frameworks.
</p>
<h2><a id="example">A Brief Example</a></h2>
<p>
In its simplest form, JEXL merges an
{@link org.apache.commons.jexl3.JexlExpression}
with a
{@link org.apache.commons.jexl3.JexlContext} when evaluating expressions.
An Expression is created using
{@link org.apache.commons.jexl3.JexlEngine#createExpression(java.lang.String)},
passing a String containing valid JEXL syntax. A simple JexlContext can be created using
a {@link org.apache.commons.jexl3.MapContext} instance;
a map of variables that will be internally wrapped can be optionally provided through its constructor.
The following example, takes a variable named 'car', and
invokes the checkStatus() method on the property 'engine'
</p>
<pre>
// Create a JexlEngine (could reuse one instead)
JexlEngine jexl = new JexlBuilder().create();
// Create an expression object equivalent to 'car.getEngine().checkStatus()':
String jexlExp = "car.engine.checkStatus()";
Expression e = jexl.createExpression( jexlExp );
// The car we have to handle coming as an argument...
Car car = theCarThatWeHandle;
// Create a context and add data
JexlContext jc = new MapContext();
jc.set("car", car );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jc);
</pre>
<h2><a id="usage">Using JEXL</a></h2>
The API is composed of three levels addressing different functional needs:
<ul>
<li>Dynamic invocation of setters, getters, methods and constructors</li>
<li>Script expressions known as JEXL expressions</li>
<li>JSP/JSF like expression known as JXLT expressions</li>
</ul>
<h3><a id="usage_note">Important note</a></h3>
The public API classes reside in the 2 packages:
<ul>
<li>org.apache.commons.jexl3</li>
<li>org.apache.commons.jexl3.introspection</li>
</ul>
<p>
The following packages follow a "use at your own maintenance cost" policy; these are only intended to be used
for extending JEXL.
Their classes and methods are not guaranteed to remain compatible in subsequent versions.
If you think you need to use directly some of their features or methods, it might be a good idea to check with
the community through the mailing list first.
</p>
<ul>
<li>org.apache.commons.jexl3.parser</li>
<li>org.apache.commons.jexl3.scripting</li>
<li>org.apache.commons.jexl3.internal</li>
<li>org.apache.commons.jexl3.internal.introspection</li>
</ul>
<h3><a id="usage_api">Dynamic Invocation</a></h3>
<p>
These functionalities are close to the core level utilities found in
<a href="https://commons.apache.org/beanutils/">BeanUtils</a>.
For basic dynamic property manipulations and method invocation, you can use the following
set of methods:
</p>
<ul>
<li>{@link org.apache.commons.jexl3.JexlEngine#newInstance}</li>
<li>{@link org.apache.commons.jexl3.JexlEngine#setProperty}</li>
<li>{@link org.apache.commons.jexl3.JexlEngine#getProperty}</li>
<li>{@link org.apache.commons.jexl3.JexlEngine#invokeMethod}</li>
</ul>
The following example illustrate their usage:
<pre>
// test outer class
public static class Froboz {
int value;
public Froboz(int v) { value = v; }
public void setValue(int v) { value = v; }
public int getValue() { return value; }
}
// test inner class
public static class Quux {
String str;
Froboz froboz;
public Quux(String str, int fro) {
this.str = str;
froboz = new Froboz(fro);
}
public Froboz getFroboz() { return froboz; }
public void setFroboz(Froboz froboz) { this.froboz = froboz; }
public String getStr() { return str; }
public void setStr(String str) { this.str = str; }
}
// test API
JexlEngine jexl = new JexlBuilder().create();
Quux quux = jexl.newInstance(Quux.class, "xuuq", 100);
jexl.setProperty(quux, "froboz.value", Integer.valueOf(100));
Object o = jexl.getProperty(quux, "froboz.value");
assertEquals("Result is not 100", new Integer(100), o);
jexl.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
o = jexl.getProperty(quux, "['froboz']['value']");
assertEquals("Result is not 1000", new Integer(1000), o);
</pre>
<h3><a id="usage_jexl">Expressions and Scripts</a></h3>
<p>
If your needs require simple expression evaluation capabilities, the core JEXL features
will most likely fit.
The main methods are:
</p>
<ul>
<li>{@link org.apache.commons.jexl3.JexlEngine#createScript}</li>
<li>{@link org.apache.commons.jexl3.JexlScript#execute}</li>
<li>{@link org.apache.commons.jexl3.JexlEngine#createExpression}</li>
<li>{@link org.apache.commons.jexl3.JexlExpression#evaluate}</li>
</ul>
The following example illustrates their usage:
<pre>
JexlEngine jexl = new JexlBuilder().create();
JexlContext jc = new MapContext();
jc.set("quuxClass", quux.class);
JexlExpression create = jexl.createExpression("quux = new(quuxClass, 'xuuq', 100)");
JelxExpression assign = jexl.createExpression("quux.froboz.value = 10");
JexlExpression check = jexl.createExpression("quux[\"froboz\"].value");
Quux quux = (Quux) create.evaluate(jc);
Object o = assign.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
o = check.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
</pre>
<h3><a id="usage_ujexl">Unified Expressions and Templates</a></h3>
<p>
If you are looking for JSP-EL like and basic templating features, you can
use Expression from a JxltEngine.
</p>
The main methods are:
<ul>
<li>{@link org.apache.commons.jexl3.JxltEngine#createExpression}</li>
<li>{@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Expression#prepare}</li>
<li>{@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Expression#evaluate}</li>
<li>{@link org.apache.commons.jexl3.JxltEngine#createTemplate}</li>
<li>{@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Template#prepare}</li>
<li>{@link aiyh.utils.tool.org.apache.commons.jexl3.JxltEngine.Template#evaluate}</li>
</ul>
The following example illustrates their usage:
<pre>
JexlEngine jexl = new JexlBuilder().create();
JxltEngine jxlt = jexl.createJxltEngine();
JxltEngine.Expression expr = jxlt.createExpression("Hello ${user}");
String hello = expr.evaluate(context).toString();
</pre>
<h3>JexlExpression, JexlScript, Expression and Template: summary</h3>
<h4>JexlExpression </h4>
<p>
These are the most basic form of JexlEngine expressions and only allow for a single command
to be executed and its result returned. If you try to use multiple commands, it ignores
everything after the first semi-colon and just returns the result from
the first command.
</p>
<p>
Also note that expressions are not statements (which is what scripts are made of) and do not allow
using the flow control (if, while, for), variables or lambdas syntactic elements.
</p>
<h4>JexlScript</h4>
<p>
These allow you to use multiple statements and you can
use variable assignments, loops, calculations, etc. More or less what can be achieved in Shell or
JavaScript at its basic level. The result from the last command is returned from the script.
</p>
<h4>JxltEngine.Expression</h4>
<p>
These are ideal to produce "one-liner" text, like a 'toString()' on steroids.
To get a calculation you use the EL-like syntax
as in ${someVariable}. The expression that goes between the brackets
behaves like a JexlScript, not an expression. You can use semi-colons to
execute multiple commands and the result from the last command is
returned from the script. You also have the ability to use a 2-pass evaluation using
the #{someScript} syntax.
</p>
<h4>JxltEngine.Template</h4>
<p>
These produce text documents. Each line beginning with '$$' (as a default) is
considered JEXL code and all others considered as JxltEngine.Expression.
Think of those as simple Velocity templates. A rewritten MudStore initial Velocity sample looks like this:
</p>
<pre><code>
&lt;html&gt;
&lt;body&gt;
Hello ${customer.name}!
&lt;table&gt;
$$ for(var mud : mudsOnSpecial ) {
$$ if (customer.hasPurchased(mud) ) {
&lt;tr&gt;
&lt;td&gt;
${flogger.getPromo( mud )}
&lt;/td&gt;
&lt;/tr&gt;
$$ }
$$ }
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h2><a id="configuration">JEXL Configuration</a></h2>
<p>
The JexlEngine can be configured through a few parameters that will drive how it reacts
in case of errors.
These configuration methods are embedded through a {@link org.apache.commons.jexl3.JexlBuilder}.
</p>
<h3><a id="static_configuration">Static &amp; Shared Configuration</a></h3>
<p>
Both JexlEngine and JxltEngine are thread-safe, most of their inner fields are final; the same instance can be shared between different
threads and proper synchronization is enforced in critical areas (introspection caches).
</p>
<p>
Of particular importance is {@link org.apache.commons.jexl3.JexlBuilder#loader} which indicates to the JexlEngine
being built which class loader to use to solve a class name; this directly affects how JexlEngine.newInstance and the 'new' script method operates.
</p>
<p>
This can also be very useful in cases where you rely on JEXL to dynamically load and call plugins for your application.
To avoid having to restart the server in case of a plugin implementation change, you can call
{@link org.apache.commons.jexl3.JexlEngine#setClassLoader} and all the scripts created through this engine instance
will automatically point to the newly loaded classes.
</p>
<p>
You can state what can be manipulated through scripting by the {@link org.apache.commons.jexl3.annotations.NoJexl}
annotation that completely shield classes and methods from JEXL introspection.
The other configurable way to restrict JEXL is by using a
{@link org.apache.commons.jexl3.introspection.JexlSandbox} which allows finer control over what is exposed; the sandbox
can be set through {@link org.apache.commons.jexl3.JexlBuilder#sandbox}.
</p>
<p>
{@link org.apache.commons.jexl3.JexlBuilder#namespaces} extends JEXL scripting by registering your own classes as
namespaces allowing your own functions to be exposed at will.
</p>
This can be used as in:
<pre><code>
public static MyMath {
public double cos(double x) {
return Math.cos(x);
}
}
Map&lt;String, Object&gt; funcs = new HashMap&lt;String, Object&gt;();
funcs.put("math", new MyMath());
JexlEngine jexl = new JexlBuilder().namespaces(funcs).create();
JexlContext jc = new MapContext();
jc.set("pi", Math.PI);
JexlExpression e = JEXL.createExpression("math:cos(pi)");
o = e.evaluate(jc);
assertEquals(Double.valueOf(-1),o);
</code></pre>
<p>
If the <i>namespace</i> is a Class and that class declares a constructor that takes a JexlContext (or
a class extending JexlContext), one <i>namespace</i> instance is created on first usage in an
expression; this instance lifetime is limited to the expression evaluation.
</p>
<p>
JexlEngine and JxltEngine expression caches can be configured as well. If you intend to use JEXL
repeatedly in your application, these are worth configuring since expression parsing is quite heavy.
Note that all caches created by JEXL are held through SoftReference; under high memory pressure, the GC will be able
to reclaim those caches and JEXL will rebuild them if needed. By default, a JexlEngine does create a cache for "small" expressions
and a JxltEngine does create one for Expression .
</p>
<p>{@link org.apache.commons.jexl3.JexlBuilder#cache} will set how many expressions can be simultaneously cached by the
JEXL engine. JxltEngine allows to define the cache size through its constructor.
</p>
<p>
{@link org.apache.commons.jexl3.JexlBuilder#debug}
makes stacktraces carried by JExlException more meaningful; in particular, these
traces will carry the exact caller location the Expression was created from.
</p>
<h3><a id="dynamic_configuration">Dynamic Configuration</a></h3>
<p>
Those configuration options can be overridden during evaluation by implementing a
{@link org.apache.commons.jexl3.JexlContext}
that also implements {@link aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options} to carry evaluation
options.
An example of such a class exists in the test package.
</p>
<p>
{@link org.apache.commons.jexl3.JexlBuilder#strict} or {@link
aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options#isStrict}
configures when JEXL considers 'null' as an error or not in various situations;
when facing an unreferenceable variable, using null as an argument to an arithmetic operator or failing to
call
a method or constructor. The lenient mode is close to JEXL-1.1 behavior.
</p>
<p>
{@link org.apache.commons.jexl3.JexlBuilder#silent} or {@link
aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options#isSilent}
configures how JEXL reacts to errors; if silent, the engine will not throw exceptions
but will warn through loggers and return null in case of errors. Note that when non-silent, JEXL throws
JexlException which are unchecked exception.
</p>
<p>
Implementing a {@link org.apache.commons.jexl3.JexlContext.NamespaceResolver} through a JexlContext - look at
<a href="https://gitbox.apache.org/repos/asf?p=commons-jexl.git;a=blob;f=src/test/java/org/apache/commons/jexl3/JexlEvalContext.java">JexlEvalContext</a>
as an example - allows to override the namespace resolution and the default namespace map defined
through {@link org.apache.commons.jexl3.JexlBuilder#namespaces}.
</p>
<h2><a id="customization">JEXL Customization</a></h2>
<p>
The {@link org.apache.commons.jexl3.JexlContext}, {@link org.apache.commons.jexl3.JexlBuilder} and
{@link aiyh.utils.tool.org.apache.commons.jexl3.JexlEngine.Options} are
the most likely interfaces you'll want to implement for customization. Since they expose variables and
options,
they are the primary targets. Before you do so, have a look at JexlEvalContext in the test directory
and {@link org.apache.commons.jexl3.ObjectContext} which may already cover some of your needs.
</p>
<p>
{@link org.apache.commons.jexl3.JexlArithmetic}
is the class to derive if you need to change how operators behave or add types upon which they
operate.
There are 3 entry points that allow customizing the type of objects created:
</p>
<ul>
<li>array literals: {@link org.apache.commons.jexl3.JexlArithmetic#arrayBuilder}</li>
<li>map literals: {@link org.apache.commons.jexl3.JexlArithmetic#mapBuilder}</li>
<li>set literals: {@link org.apache.commons.jexl3.JexlArithmetic#setBuilder}</li>
<li>range objects: {@link org.apache.commons.jexl3.JexlArithmetic#createRange}</li>
</ul>
<p>
You can also overload operator methods; by convention, each operator has a method name associated to it.
If you overload some in your JexlArithmetic derived implementation, these methods will be called when the
arguments match your method signature.
For example, this would be the case if you wanted '+' to operate on arrays; you'd need to derive
JexlArithmetic and implement 'public Object add(Set&lt;?;&gt; x, Set&lt;?;&gt; y)' method.
Note however that you can <em>not</em> change the operator precedence.
The list of operator / method matches is the following:
</p>
<table><caption>Operators</caption>
<tr>
<th>Operator</th>
<th>Method Name</th>
<th>Example</th>
</tr>
<tr>
<td>+</td>
<td>add</td>
<td>add(x, y)</td>
</tr>
<tr>
<td>-</td>
<td>subtract</td>
<td>subtract(x, y)</td>
</tr>
<tr>
<td>*</td>
<td>multiply</td>
<td>multiply(x, y)</td>
</tr>
<tr>
<td>/</td>
<td>divide</td>
<td>divide(x, y)</td>
</tr>
<tr>
<td>%</td>
<td>mod</td>
<td>mod(x, y)</td>
</tr>
<tr>
<td>&amp;</td>
<td>bitwiseAnd</td>
<td>bitwiseAnd(x, y)</td>
</tr>
<tr>
<td>|</td>
<td>bitwiseOr</td>
<td>bitwiseOr(x, y)</td>
</tr>
<tr>
<td>^</td>
<td>bitwiseXor</td>
<td>bitwiseXor(x, y)</td>
</tr>
<tr>
<td>!</td>
<td>logicalNot</td>
<td>logicalNot(x)</td>
</tr>
<tr>
<td>-</td>
<td>bitwiseComplement</td>
<td>bitiwiseComplement(x)</td>
</tr>
<tr>
<td>==</td>
<td>equals</td>
<td>equals(x, y)</td>
</tr>
<tr>
<td>&lt;</td>
<td>lessThan</td>
<td>lessThan(x, y)</td>
</tr>
<tr>
<td>&lt;=</td>
<td>lessThanOrEqual</td>
<td>lessThanOrEqual(x, y)</td>
</tr>
<tr>
<td>&gt;</td>
<td>greaterThan</td>
<td>greaterThan(x, y)</td>
</tr>
<tr>
<td>&gt;=</td>
<td>greaterThanOrEqual</td>
<td>greaterThanOrEqual(x, y)</td>
</tr>
<tr>
<td>-</td>
<td>negate</td>
<td>negate(x)</td>
</tr>
<tr>
<td>size</td>
<td>size</td>
<td>size(x)</td>
</tr>
<tr>
<td>empty</td>
<td>empty</td>
<td>empty(x)</td>
</tr>
</table>
<p>
You can also add methods to overload property getters and setters operators behaviors.
Public methods of the JexlArithmetic instance named propertyGet/propertySet/arrayGet/arraySet are potential
overrides that will be called when appropriate.
The following table is an overview of the relation between a syntactic form and the method to call
where V is the property value class, O the object class and P the property identifier class (usually String or Integer).
</p>
<table><caption>Property Accessors</caption>
<tr>
<th>Expression</th>
<th>Method Template</th>
</tr>
<tr>
<td>foo.property</td>
<td>public V propertyGet(O obj, P property);</td>
</tr>
<tr>
<td>foo.property = value</td>
<td>public V propertySet(O obj, P property, V value);</td>
</tr>
<tr>
<td>foo[property]</td>
<td>public V arrayGet(O obj, P property, V value);</td>
</tr>
<tr>
<td>foo[property] = value</td>
<td>public V arraySet(O obj, P property, V value);</td>
</tr>
</table>
<p>
You can also override the base operator methods, those whose arguments are Object which gives you total
control.
</p>
<h2><a id="extension">Extending JEXL</a></h2>
If you need to make JEXL treat some objects in a specialized manner or tweak how it
reacts to some settings, you can derive most of its inner-workings. The classes and methods are rarely private or
final - only when the inner contract really requires it. However, using the protected methods
and internal package classes imply you might have to re-adapt your code when new JEXL versions are released.
<p>
{@link org.apache.commons.jexl3.internal.Engine} can be
extended to let you capture your own configuration defaults wrt cache sizes and various flags.
Implementing your own cache - instead of the basic LinkedHashMap based one - would be
another possible extension.
</p>
<p>
{@link org.apache.commons.jexl3.internal.Interpreter}
is the class to derive if you need to add more features to the evaluation
itself; for instance, you want pre- and post- resolvers for variables or nested scopes for
for variable contexts.
</p>
<p>
{@link org.apache.commons.jexl3.internal.introspection.Uberspect}
is the class to derive if you need to add introspection or reflection capabilities for some objects, for
instance adding factory based support to the 'new' operator.
The code already reflects public fields as properties on top of Java-beans conventions.
</p>
</body>
</html>

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTAddNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTAddNode extends JexlNode {
public ASTAddNode(int id) {
super(id);
}
public ASTAddNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=806bab8d64feb5b185229e22923f6ad8 (do not edit this line) */

View File

@ -0,0 +1,34 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.parser;
public final class ASTAmbiguous extends JexlNode {
ASTAmbiguous(final int id) {
super(id);
}
ASTAmbiguous(final Parser p, final int id) {
super(p, id);
}
@Override
public Object jjtAccept(final ParserVisitor visitor, final Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTAndNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTAndNode extends JexlNode {
public ASTAndNode(int id) {
super(id);
}
public ASTAndNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=40b6ccfc8a7653590cc725e7b1494630 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTAnnotatedStatement.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTAnnotatedStatement extends JexlNode {
public ASTAnnotatedStatement(int id) {
super(id);
}
public ASTAnnotatedStatement(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=a4b6f99ce64d9977701193a577e07147 (do not edit this line) */

View File

@ -0,0 +1,54 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.parser;
/**
* Annotation.
*/
public class ASTAnnotation extends JexlNode {
private String name = null;
ASTAnnotation(final int id) {
super(id);
}
ASTAnnotation(final Parser p, final int id) {
super(p, id);
}
@Override
public String toString() {
return name;
}
void setName(final String identifier) {
if (identifier.charAt(0) == '@') {
name = identifier.substring(1);
} else {
name = identifier;
}
}
public String getName() {
return name;
}
@Override
public Object jjtAccept(final ParserVisitor visitor, final Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTArguments.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTArguments extends JexlNode {
public ASTArguments(int id) {
super(id);
}
public ASTArguments(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=f3e0f0702d6213b0deb816ec8c6c77e1 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTArrayAccess.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTArrayAccess extends JexlNode {
public ASTArrayAccess(int id) {
super(id);
}
public ASTArrayAccess(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=99079363d4bda2f6e895bd0d1ec02ad0 (do not edit this line) */

View File

@ -0,0 +1,64 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.parser;
import aiyh.utils.tool.org.apache.commons.jexl3.internal.Debugger;
/**
* An array literal.
*/
public final class ASTArrayLiteral extends JexlNode {
/** Whether this array is constant or not. */
private boolean constant = false;
ASTArrayLiteral(final int id) {
super(id);
}
ASTArrayLiteral(final Parser p, final int id) {
super(p, id);
}
@Override
public String toString() {
final Debugger dbg = new Debugger();
return dbg.data(this);
}
@Override
protected boolean isConstant(final boolean literal) {
return constant;
}
@Override
public void jjtClose() {
constant = true;
for (int c = 0; c < jjtGetNumChildren() && constant; ++c) {
final JexlNode child = jjtGetChild(c);
if (child instanceof ASTReference) {
constant = child.isConstant(true);
} else if (!child.isConstant()) {
constant = false;
}
}
}
@Override
public Object jjtAccept(final ParserVisitor visitor, final Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTAssignment.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTAssignment extends JexlNode {
public ASTAssignment(int id) {
super(id);
}
public ASTAssignment(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=dfc31f1d65291e39f709e9d7b8af723e (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTBitwiseAndNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTBitwiseAndNode extends JexlNode {
public ASTBitwiseAndNode(int id) {
super(id);
}
public ASTBitwiseAndNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=73cab80f3e9aa8c9d4756518457f1f62 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTBitwiseComplNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTBitwiseComplNode extends JexlNode {
public ASTBitwiseComplNode(int id) {
super(id);
}
public ASTBitwiseComplNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=26eb7afeff3a890bd89a308bd3baaff4 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTBitwiseOrNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTBitwiseOrNode extends JexlNode {
public ASTBitwiseOrNode(int id) {
super(id);
}
public ASTBitwiseOrNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=29bc374c952cfb8d5f1300388b826894 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTBitwiseXorNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTBitwiseXorNode extends JexlNode {
public ASTBitwiseXorNode(int id) {
super(id);
}
public ASTBitwiseXorNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=82b605fc2163491bb0de4206592a9dcb (do not edit this line) */

View File

@ -0,0 +1,36 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.parser;
/**
* Declares a block.
*/
public class ASTBlock extends JexlLexicalNode {
public ASTBlock(final int id) {
super(id);
}
public ASTBlock(final Parser p, final int id) {
super(p, id);
}
@Override
public Object jjtAccept(final ParserVisitor visitor, final Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTBreak.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTBreak extends JexlNode {
public ASTBreak(int id) {
super(id);
}
public ASTBreak(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=436506a6afa286186b15cc92716f6ace (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTConstructorNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTConstructorNode extends JexlNode {
public ASTConstructorNode(int id) {
super(id);
}
public ASTConstructorNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=70bc27721984d7019eed5bf429966764 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTContinue.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTContinue extends JexlNode {
public ASTContinue(int id) {
super(id);
}
public ASTContinue(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=fef9263bbdb7e5c0628ea4eb289ea023 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTDivNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTDivNode extends JexlNode {
public ASTDivNode(int id) {
super(id);
}
public ASTDivNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=11e33898e07b9cc8bb68e2d83b4733c4 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTDoWhileStatement.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTDoWhileStatement extends JexlNode {
public ASTDoWhileStatement(int id) {
super(id);
}
public ASTDoWhileStatement(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=a5f9770b80d8e9585817452b8b4e3f07 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTEQNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTEQNode extends JexlNode {
public ASTEQNode(int id) {
super(id);
}
public ASTEQNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=b834385ba52e4b0493b9b29c08260092 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTERNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTERNode extends JexlNode {
public ASTERNode(int id) {
super(id);
}
public ASTERNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=d1d28a19686e08a7e2d769e1b48e4ac8 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTEWNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTEWNode extends JexlNode {
public ASTEWNode(int id) {
super(id);
}
public ASTEWNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=6b784152590211436433bd8dd544f8a0 (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTEmptyFunction.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTEmptyFunction extends JexlNode {
public ASTEmptyFunction(int id) {
super(id);
}
public ASTEmptyFunction(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=1638f1cd2222b1635675576d3784d87a (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTExtendedLiteral.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTExtendedLiteral extends JexlNode {
public ASTExtendedLiteral(int id) {
super(id);
}
public ASTExtendedLiteral(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=32ae9984ba69f72b0cf1b4122ec9946f (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTFalseNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTFalseNode extends JexlNode {
public ASTFalseNode(int id) {
super(id);
}
public ASTFalseNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=6826c5e1f1f05de07085361c398ab664 (do not edit this line) */

View File

@ -0,0 +1,37 @@
/*
* 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 aiyh.utils.tool.org.apache.commons.jexl3.parser;
/**
* Declares a for each loop.
*/
public class ASTForeachStatement extends JexlLexicalNode {
public ASTForeachStatement(final int id) {
super(id);
}
public ASTForeachStatement(final Parser p, final int id) {
super(p, id);
}
@Override
public Object jjtAccept(final ParserVisitor visitor, final Object data) {
return visitor.visit(this, data);
}
}

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTFunctionNode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTFunctionNode extends JexlNode {
public ASTFunctionNode(int id) {
super(id);
}
public ASTFunctionNode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=c045697b9bdb20ca4cac761e749e164e (do not edit this line) */

View File

@ -0,0 +1,22 @@
/* Generated by: JJTree: Do not edit this line. ASTGENode.java Version 1.1 */
/* ParserGeneratorCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
package aiyh.utils.tool.org.apache.commons.jexl3.parser;
public
class ASTGENode extends JexlNode {
public ASTGENode(int id) {
super(id);
}
public ASTGENode(Parser p, int id) {
super(p, id);
}
/** Accept the visitor. **/
public Object jjtAccept(ParserVisitor visitor, Object data) {
return
visitor.visit(this, data);
}
}
/* ParserGeneratorCC - OriginalChecksum=dd856035ccad3970bc25804016f791dd (do not edit this line) */

Some files were not shown because too many files have changed in this diff Show More