添加jexl依赖,添加脚本执行器
parent
8c84a01a69
commit
2bbac377aa
File diff suppressed because it is too large
Load Diff
|
@ -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" & "not-strict":
|
||||||
|
* <p> 0 & null should be indicators of "default" values so that even in an case of error,
|
||||||
|
* something meaningful can still be inferred; may be convenient for configurations.
|
||||||
|
* </p>
|
||||||
|
* </li>
|
||||||
|
* <li>When "silent" & "strict":
|
||||||
|
* <p>One should probably consider using null as an error case - ie, every object
|
||||||
|
* manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
|
||||||
|
* can be used to workaround exceptional cases.
|
||||||
|
* Use case could be configuration with no implicit values or defaults.
|
||||||
|
* </p>
|
||||||
|
* </li>
|
||||||
|
* <li>When "not-silent" & "not-strict":
|
||||||
|
* <p>The error control grain is roughly on par with JEXL 1.0</p>
|
||||||
|
* </li>
|
||||||
|
* <li>When "not-silent" & "strict":
|
||||||
|
* <p>The finest error control grain is obtained; it is the closest to Java code -
|
||||||
|
* still augmented by "script" capabilities regarding automated conversions and type matching.
|
||||||
|
* </p>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class JexlBuilder {
|
||||||
|
|
||||||
|
/** The default maximum expression length to hit the expression cache. */
|
||||||
|
protected static final int CACHE_THRESHOLD = 64;
|
||||||
|
|
||||||
|
/** The JexlUberspect instance. */
|
||||||
|
private JexlUberspect uberspect = null;
|
||||||
|
|
||||||
|
/** The strategy strategy. */
|
||||||
|
private JexlUberspect.ResolverStrategy strategy = null;
|
||||||
|
|
||||||
|
/** The sandbox. */
|
||||||
|
private JexlSandbox sandbox = null;
|
||||||
|
|
||||||
|
/** The Log to which all JexlEngine messages will be logged. */
|
||||||
|
private Log logger = null;
|
||||||
|
|
||||||
|
/** Whether 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
@ -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);
|
||||||
|
}
|
|
@ -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 (()->{...}, 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 (->,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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 & 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 < 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 <= 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 > 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 >= 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 &= 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<Object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 > 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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <=> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 > 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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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 "everything is allowed" when
|
||||||
|
* a block-box considers no permissions as "nothing is allowed".
|
||||||
|
* @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 (<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
|
@ -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>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
Hello ${customer.name}!
|
||||||
|
<table>
|
||||||
|
$$ for(var mud : mudsOnSpecial ) {
|
||||||
|
$$ if (customer.hasPurchased(mud) ) {
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
${flogger.getPromo( mud )}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
$$ }
|
||||||
|
$$ }
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</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 & 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<String, Object> funcs = new HashMap<String, Object>();
|
||||||
|
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<?;> x, Set<?;> 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>&</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><</td>
|
||||||
|
<td>lessThan</td>
|
||||||
|
<td>lessThan(x, y)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><=</td>
|
||||||
|
<td>lessThanOrEqual</td>
|
||||||
|
<td>lessThanOrEqual(x, y)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>></td>
|
||||||
|
<td>greaterThan</td>
|
||||||
|
<td>greaterThan(x, y)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>>=</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>
|
|
@ -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) */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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) */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) */
|
|
@ -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
Loading…
Reference in New Issue