This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,401 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.apache.el.util.MessageFactory;
/**
* A helper class of Arithmetic defined by the EL Specification
* @author Jacob Hookom [jacob@hookom.net]
*/
public abstract class ELArithmetic {
public static final class BigDecimalDelegate extends ELArithmetic {
@Override
protected Number add(Number num0, Number num1) {
return ((BigDecimal) num0).add((BigDecimal) num1);
}
@Override
protected Number coerce(Number num) {
if (num instanceof BigDecimal)
return num;
if (num instanceof BigInteger)
return new BigDecimal((BigInteger) num);
return new BigDecimal(num.doubleValue());
}
@Override
protected Number coerce(String str) {
return new BigDecimal(str);
}
@Override
protected Number divide(Number num0, Number num1) {
return ((BigDecimal) num0).divide((BigDecimal) num1,
RoundingMode.HALF_UP);
}
@Override
protected Number subtract(Number num0, Number num1) {
return ((BigDecimal) num0).subtract((BigDecimal) num1);
}
@Override
protected Number mod(Number num0, Number num1) {
return Double.valueOf(num0.doubleValue() % num1.doubleValue());
}
@Override
protected Number multiply(Number num0, Number num1) {
return ((BigDecimal) num0).multiply((BigDecimal) num1);
}
@Override
public boolean matches(Object obj0, Object obj1) {
return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
}
}
public static final class BigIntegerDelegate extends ELArithmetic {
@Override
protected Number add(Number num0, Number num1) {
return ((BigInteger) num0).add((BigInteger) num1);
}
@Override
protected Number coerce(Number num) {
if (num instanceof BigInteger)
return num;
return new BigInteger(num.toString());
}
@Override
protected Number coerce(String str) {
return new BigInteger(str);
}
@Override
protected Number divide(Number num0, Number num1) {
return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), RoundingMode.HALF_UP);
}
@Override
protected Number multiply(Number num0, Number num1) {
return ((BigInteger) num0).multiply((BigInteger) num1);
}
@Override
protected Number mod(Number num0, Number num1) {
return ((BigInteger) num0).mod((BigInteger) num1);
}
@Override
protected Number subtract(Number num0, Number num1) {
return ((BigInteger) num0).subtract((BigInteger) num1);
}
@Override
public boolean matches(Object obj0, Object obj1) {
return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
}
}
public static final class DoubleDelegate extends ELArithmetic {
@Override
protected Number add(Number num0, Number num1) {
// could only be one of these
if (num0 instanceof BigDecimal) {
return ((BigDecimal) num0).add(new BigDecimal(num1.doubleValue()));
} else if (num1 instanceof BigDecimal) {
return ((new BigDecimal(num0.doubleValue()).add((BigDecimal) num1)));
}
return Double.valueOf(num0.doubleValue() + num1.doubleValue());
}
@Override
protected Number coerce(Number num) {
if (num instanceof Double)
return num;
if (num instanceof BigInteger)
return new BigDecimal((BigInteger) num);
return Double.valueOf(num.doubleValue());
}
@Override
protected Number coerce(String str) {
return Double.valueOf(str);
}
@Override
protected Number divide(Number num0, Number num1) {
return Double.valueOf(num0.doubleValue() / num1.doubleValue());
}
@Override
protected Number mod(Number num0, Number num1) {
return Double.valueOf(num0.doubleValue() % num1.doubleValue());
}
@Override
protected Number subtract(Number num0, Number num1) {
// could only be one of these
if (num0 instanceof BigDecimal) {
return ((BigDecimal) num0).subtract(new BigDecimal(num1.doubleValue()));
} else if (num1 instanceof BigDecimal) {
return ((new BigDecimal(num0.doubleValue()).subtract((BigDecimal) num1)));
}
return Double.valueOf(num0.doubleValue() - num1.doubleValue());
}
@Override
protected Number multiply(Number num0, Number num1) {
// could only be one of these
if (num0 instanceof BigDecimal) {
return ((BigDecimal) num0).multiply(new BigDecimal(num1.doubleValue()));
} else if (num1 instanceof BigDecimal) {
return ((new BigDecimal(num0.doubleValue()).multiply((BigDecimal) num1)));
}
return Double.valueOf(num0.doubleValue() * num1.doubleValue());
}
@Override
public boolean matches(Object obj0, Object obj1) {
return (obj0 instanceof Double
|| obj1 instanceof Double
|| obj0 instanceof Float
|| obj1 instanceof Float
|| (obj0 instanceof String && ELSupport
.isStringFloat((String) obj0)) || (obj1 instanceof String && ELSupport
.isStringFloat((String) obj1)));
}
}
public static final class LongDelegate extends ELArithmetic {
@Override
protected Number add(Number num0, Number num1) {
return Long.valueOf(num0.longValue() + num1.longValue());
}
@Override
protected Number coerce(Number num) {
if (num instanceof Long)
return num;
return Long.valueOf(num.longValue());
}
@Override
protected Number coerce(String str) {
return Long.valueOf(str);
}
@Override
protected Number divide(Number num0, Number num1) {
return Long.valueOf(num0.longValue() / num1.longValue());
}
@Override
protected Number mod(Number num0, Number num1) {
return Long.valueOf(num0.longValue() % num1.longValue());
}
@Override
protected Number subtract(Number num0, Number num1) {
return Long.valueOf(num0.longValue() - num1.longValue());
}
@Override
protected Number multiply(Number num0, Number num1) {
return Long.valueOf(num0.longValue() * num1.longValue());
}
@Override
public boolean matches(Object obj0, Object obj1) {
return (obj0 instanceof Long || obj1 instanceof Long);
}
}
public static final BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate();
public static final BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate();
public static final DoubleDelegate DOUBLE = new DoubleDelegate();
public static final LongDelegate LONG = new LongDelegate();
private static final Long ZERO = Long.valueOf(0);
public static final Number add(final Object obj0, final Object obj1) {
final ELArithmetic delegate = findDelegate(obj0, obj1);
if (delegate == null) {
return Long.valueOf(0);
}
Number num0 = delegate.coerce(obj0);
Number num1 = delegate.coerce(obj1);
return delegate.add(num0, num1);
}
public static final Number mod(final Object obj0, final Object obj1) {
if (obj0 == null && obj1 == null) {
return Long.valueOf(0);
}
final ELArithmetic delegate;
if (BIGDECIMAL.matches(obj0, obj1))
delegate = DOUBLE;
else if (DOUBLE.matches(obj0, obj1))
delegate = DOUBLE;
else if (BIGINTEGER.matches(obj0, obj1))
delegate = BIGINTEGER;
else
delegate = LONG;
Number num0 = delegate.coerce(obj0);
Number num1 = delegate.coerce(obj1);
return delegate.mod(num0, num1);
}
public static final Number subtract(final Object obj0, final Object obj1) {
final ELArithmetic delegate = findDelegate(obj0, obj1);
if (delegate == null) {
return Long.valueOf(0);
}
Number num0 = delegate.coerce(obj0);
Number num1 = delegate.coerce(obj1);
return delegate.subtract(num0, num1);
}
public static final Number divide(final Object obj0, final Object obj1) {
if (obj0 == null && obj1 == null) {
return ZERO;
}
final ELArithmetic delegate;
if (BIGDECIMAL.matches(obj0, obj1))
delegate = BIGDECIMAL;
else if (BIGINTEGER.matches(obj0, obj1))
delegate = BIGDECIMAL;
else
delegate = DOUBLE;
Number num0 = delegate.coerce(obj0);
Number num1 = delegate.coerce(obj1);
return delegate.divide(num0, num1);
}
public static final Number multiply(final Object obj0, final Object obj1) {
final ELArithmetic delegate = findDelegate(obj0, obj1);
if (delegate == null) {
return Long.valueOf(0);
}
Number num0 = delegate.coerce(obj0);
Number num1 = delegate.coerce(obj1);
return delegate.multiply(num0, num1);
}
private static ELArithmetic findDelegate(final Object obj0, final Object obj1) {
if (obj0 == null && obj1 == null) {
return null;
}
if (BIGDECIMAL.matches(obj0, obj1)) {
return BIGDECIMAL;
} else if (DOUBLE.matches(obj0, obj1)) {
if (BIGINTEGER.matches(obj0, obj1)) {
return BIGDECIMAL;
} else {
return DOUBLE;
}
} else if (BIGINTEGER.matches(obj0, obj1)) {
return BIGINTEGER;
} else {
return LONG;
}
}
public static final boolean isNumber(final Object obj) {
return (obj != null && isNumberType(obj.getClass()));
}
public static final boolean isNumberType(final Class<?> type) {
return type == Long.TYPE || type == Double.TYPE ||
type == Byte.TYPE || type == Short.TYPE ||
type == Integer.TYPE || type == Float.TYPE ||
Number.class.isAssignableFrom(type);
}
/**
*
*/
protected ELArithmetic() {
super();
}
protected abstract Number add(final Number num0, final Number num1);
protected abstract Number multiply(final Number num0, final Number num1);
protected abstract Number subtract(final Number num0, final Number num1);
protected abstract Number mod(final Number num0, final Number num1);
protected abstract Number coerce(final Number num);
protected final Number coerce(final Object obj) {
if (isNumber(obj)) {
return coerce((Number) obj);
}
if (obj == null || "".equals(obj)) {
return coerce(ZERO);
}
if (obj instanceof String) {
return coerce((String) obj);
}
if (obj instanceof Character) {
return coerce(Short.valueOf((short) ((Character) obj).charValue()));
}
throw new IllegalArgumentException(MessageFactory.get("error.convert",
obj, obj.getClass(), "Number"));
}
protected abstract Number coerce(final String str);
protected abstract Number divide(final Number num0, final Number num1);
protected abstract boolean matches(final Object obj0, final Object obj1);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.EvaluationListener;
import javax.el.FunctionMapper;
import javax.el.ImportHandler;
import javax.el.VariableMapper;
public final class EvaluationContext extends ELContext {
private final ELContext elContext;
private final FunctionMapper fnMapper;
private final VariableMapper varMapper;
public EvaluationContext(ELContext elContext, FunctionMapper fnMapper,
VariableMapper varMapper) {
this.elContext = elContext;
this.fnMapper = fnMapper;
this.varMapper = varMapper;
}
public ELContext getELContext() {
return elContext;
}
@Override
public FunctionMapper getFunctionMapper() {
return fnMapper;
}
@Override
public VariableMapper getVariableMapper() {
return varMapper;
}
@Override
// Can't use Class<?> because API needs to match specification in superclass
public Object getContext(@SuppressWarnings("rawtypes") Class key) {
return elContext.getContext(key);
}
@Override
public ELResolver getELResolver() {
return elContext.getELResolver();
}
@Override
public boolean isPropertyResolved() {
return elContext.isPropertyResolved();
}
@Override
// Can't use Class<?> because API needs to match specification in superclass
public void putContext(@SuppressWarnings("rawtypes") Class key,
Object contextObject) {
elContext.putContext(key, contextObject);
}
@Override
public void setPropertyResolved(boolean resolved) {
elContext.setPropertyResolved(resolved);
}
@Override
public Locale getLocale() {
return elContext.getLocale();
}
@Override
public void setLocale(Locale locale) {
elContext.setLocale(locale);
}
@Override
public void setPropertyResolved(Object base, Object property) {
elContext.setPropertyResolved(base, property);
}
@Override
public ImportHandler getImportHandler() {
return elContext.getImportHandler();
}
@Override
public void addEvaluationListener(EvaluationListener listener) {
elContext.addEvaluationListener(listener);
}
@Override
public List<EvaluationListener> getEvaluationListeners() {
return elContext.getEvaluationListeners();
}
@Override
public void notifyBeforeEvaluation(String expression) {
elContext.notifyBeforeEvaluation(expression);
}
@Override
public void notifyAfterEvaluation(String expression) {
elContext.notifyAfterEvaluation(expression);
}
@Override
public void notifyPropertyResolved(Object base, Object property) {
elContext.notifyPropertyResolved(base, property);
}
@Override
public boolean isLambdaArgument(String name) {
return elContext.isLambdaArgument(name);
}
@Override
public Object getLambdaArgument(String name) {
return elContext.getLambdaArgument(name);
}
@Override
public void enterLambdaScope(Map<String, Object> arguments) {
elContext.enterLambdaScope(arguments);
}
@Override
public void exitLambdaScope() {
elContext.exitLambdaScope();
}
@Override
public Object convertToType(Object obj, Class<?> type) {
return elContext.convertToType(obj, type);
}
}

View File

@@ -0,0 +1,337 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.FunctionMapper;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.el.VariableMapper;
import org.apache.el.MethodExpressionImpl;
import org.apache.el.MethodExpressionLiteral;
import org.apache.el.ValueExpressionImpl;
import org.apache.el.parser.AstDeferredExpression;
import org.apache.el.parser.AstDynamicExpression;
import org.apache.el.parser.AstFunction;
import org.apache.el.parser.AstIdentifier;
import org.apache.el.parser.AstLiteralExpression;
import org.apache.el.parser.AstValue;
import org.apache.el.parser.ELParser;
import org.apache.el.parser.Node;
import org.apache.el.parser.NodeVisitor;
import org.apache.el.util.ConcurrentCache;
import org.apache.el.util.MessageFactory;
/**
* @author Jacob Hookom [jacob@hookom.net]
*/
public final class ExpressionBuilder implements NodeVisitor {
private static final SynchronizedStack<ELParser> parserCache = new SynchronizedStack<>();
private static final int CACHE_SIZE;
private static final String CACHE_SIZE_PROP =
"org.apache.el.ExpressionBuilder.CACHE_SIZE";
static {
String cacheSizeStr;
if (System.getSecurityManager() == null) {
cacheSizeStr = System.getProperty(CACHE_SIZE_PROP, "5000");
} else {
cacheSizeStr = AccessController.doPrivileged(
new PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty(CACHE_SIZE_PROP, "5000");
}
});
}
CACHE_SIZE = Integer.parseInt(cacheSizeStr);
}
private static final ConcurrentCache<String, Node> expressionCache =
new ConcurrentCache<>(CACHE_SIZE);
private FunctionMapper fnMapper;
private VariableMapper varMapper;
private final String expression;
public ExpressionBuilder(String expression, ELContext ctx)
throws ELException {
this.expression = expression;
FunctionMapper ctxFn = ctx.getFunctionMapper();
VariableMapper ctxVar = ctx.getVariableMapper();
if (ctxFn != null) {
this.fnMapper = new FunctionMapperFactory(ctxFn);
}
if (ctxVar != null) {
this.varMapper = new VariableMapperFactory(ctxVar);
}
}
public static final Node createNode(String expr) throws ELException {
Node n = createNodeInternal(expr);
return n;
}
private static final Node createNodeInternal(String expr)
throws ELException {
if (expr == null) {
throw new ELException(MessageFactory.get("error.null"));
}
Node n = expressionCache.get(expr);
if (n == null) {
ELParser parser = parserCache.pop();
try {
if (parser == null) {
parser = new ELParser(new StringReader(expr));
} else {
parser.ReInit(new StringReader(expr));
}
n = parser.CompositeExpression();
// validate composite expression
int numChildren = n.jjtGetNumChildren();
if (numChildren == 1) {
n = n.jjtGetChild(0);
} else {
Class<?> type = null;
Node child = null;
for (int i = 0; i < numChildren; i++) {
child = n.jjtGetChild(i);
if (child instanceof AstLiteralExpression)
continue;
if (type == null)
type = child.getClass();
else {
if (!type.equals(child.getClass())) {
throw new ELException(MessageFactory.get(
"error.mixed", expr));
}
}
}
}
if (n instanceof AstDeferredExpression
|| n instanceof AstDynamicExpression) {
n = n.jjtGetChild(0);
}
expressionCache.put(expr, n);
} catch (Exception e) {
throw new ELException(
MessageFactory.get("error.parseFail", expr), e);
} finally {
if (parser != null) {
parserCache.push(parser);
}
}
}
return n;
}
private void prepare(Node node) throws ELException {
try {
node.accept(this);
} catch (Exception e) {
if (e instanceof ELException) {
throw (ELException) e;
} else {
throw (new ELException(e));
}
}
if (this.fnMapper instanceof FunctionMapperFactory) {
this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create();
}
if (this.varMapper instanceof VariableMapperFactory) {
this.varMapper = ((VariableMapperFactory) this.varMapper).create();
}
}
private Node build() throws ELException {
Node n = createNodeInternal(this.expression);
this.prepare(n);
if (n instanceof AstDeferredExpression
|| n instanceof AstDynamicExpression) {
n = n.jjtGetChild(0);
}
return n;
}
/*
* (non-Javadoc)
*
* @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node)
*/
@Override
public void visit(Node node) throws ELException {
if (node instanceof AstFunction) {
AstFunction funcNode = (AstFunction) node;
Method m = null;
if (this.fnMapper != null) {
m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode
.getLocalName());
}
// References to variables that refer to lambda expressions will be
// parsed as functions. This is handled at runtime but at this point
// need to treat it as a variable rather than a function.
if (m == null && this.varMapper != null &&
funcNode.getPrefix().length() == 0) {
this.varMapper.resolveVariable(funcNode.getLocalName());
return;
}
if (this.fnMapper == null) {
throw new ELException(MessageFactory.get("error.fnMapper.null"));
}
if (m == null) {
throw new ELException(MessageFactory.get(
"error.fnMapper.method", funcNode.getOutputName()));
}
int methodParameterCount = m.getParameterTypes().length;
// AstFunction->MethodParameters->Parameters()
int inputParameterCount = node.jjtGetChild(0).jjtGetNumChildren();
if (m.isVarArgs() && inputParameterCount < methodParameterCount - 1 ||
!m.isVarArgs() && inputParameterCount != methodParameterCount) {
throw new ELException(MessageFactory.get(
"error.fnMapper.paramcount", funcNode.getOutputName(),
"" + methodParameterCount, "" + node.jjtGetChild(0).jjtGetNumChildren()));
}
} else if (node instanceof AstIdentifier && this.varMapper != null) {
String variable = ((AstIdentifier) node).getImage();
// simply capture it
this.varMapper.resolveVariable(variable);
}
}
public ValueExpression createValueExpression(Class<?> expectedType)
throws ELException {
Node n = this.build();
return new ValueExpressionImpl(this.expression, n, this.fnMapper,
this.varMapper, expectedType);
}
public MethodExpression createMethodExpression(Class<?> expectedReturnType,
Class<?>[] expectedParamTypes) throws ELException {
Node n = this.build();
if (!n.isParametersProvided() && expectedParamTypes == null) {
throw new NullPointerException(MessageFactory
.get("error.method.nullParms"));
}
if (n instanceof AstValue || n instanceof AstIdentifier) {
return new MethodExpressionImpl(expression, n, this.fnMapper,
this.varMapper, expectedReturnType, expectedParamTypes);
} else if (n instanceof AstLiteralExpression) {
return new MethodExpressionLiteral(expression, expectedReturnType,
expectedParamTypes);
} else {
throw new ELException("Not a Valid Method Expression: "
+ expression);
}
}
/*
* Copied from org.apache.tomcat.util.collections.SynchronizedStack since
* we don't want the EL implementation to depend on the JAR where that
* class resides.
*/
private static class SynchronizedStack<T> {
public static final int DEFAULT_SIZE = 128;
private static final int DEFAULT_LIMIT = -1;
private int size;
private final int limit;
/*
* Points to the next available object in the stack
*/
private int index = -1;
private Object[] stack;
public SynchronizedStack() {
this(DEFAULT_SIZE, DEFAULT_LIMIT);
}
public SynchronizedStack(int size, int limit) {
this.size = size;
this.limit = limit;
stack = new Object[size];
}
public synchronized boolean push(T obj) {
index++;
if (index == size) {
if (limit == -1 || size < limit) {
expand();
} else {
index--;
return false;
}
}
stack[index] = obj;
return true;
}
@SuppressWarnings("unchecked")
public synchronized T pop() {
if (index == -1) {
return null;
}
T result = (T) stack[index];
stack[index--] = null;
return result;
}
private void expand() {
int newSize = size * 2;
if (limit != -1 && newSize > limit) {
newSize = limit;
}
Object[] newStack = new Object[newSize];
System.arraycopy(stack, 0, newStack, 0, size);
// This is the only point where garbage is created by throwing away the
// old array. Note it is only the array, not the contents, that becomes
// garbage.
stack = newStack;
size = newSize;
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.lang.reflect.Method;
import javax.el.FunctionMapper;
/**
* @author Jacob Hookom [jacob@hookom.net]
*/
public class FunctionMapperFactory extends FunctionMapper {
protected FunctionMapperImpl memento = null;
protected final FunctionMapper target;
public FunctionMapperFactory(FunctionMapper mapper) {
if (mapper == null) {
throw new NullPointerException("FunctionMapper target cannot be null");
}
this.target = mapper;
}
/* (non-Javadoc)
* @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String)
*/
@Override
public Method resolveFunction(String prefix, String localName) {
if (this.memento == null) {
this.memento = new FunctionMapperImpl();
}
Method m = this.target.resolveFunction(prefix, localName);
if (m != null) {
this.memento.mapFunction(prefix, localName, m);
}
return m;
}
@Override
public void mapFunction(String prefix, String localName, Method method) {
if (this.memento == null) {
this.memento = new FunctionMapperImpl();
}
memento.mapFunction(prefix, localName, method);
}
public FunctionMapper create() {
return this.memento;
}
}

View File

@@ -0,0 +1,188 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.el.FunctionMapper;
import org.apache.el.util.ReflectionUtil;
/**
* @author Jacob Hookom [jacob@hookom.net]
*/
public class FunctionMapperImpl extends FunctionMapper implements
Externalizable {
private static final long serialVersionUID = 1L;
protected ConcurrentMap<String, Function> functions = new ConcurrentHashMap<>();
/*
* (non-Javadoc)
*
* @see javax.el.FunctionMapper#resolveFunction(java.lang.String,
* java.lang.String)
*/
@Override
public Method resolveFunction(String prefix, String localName) {
Function f = this.functions.get(prefix + ":" + localName);
if (f == null) {
return null;
}
return f.getMethod();
}
@Override
public void mapFunction(String prefix, String localName, Method m) {
String key = prefix + ":" + localName;
if (m == null) {
functions.remove(key);
} else {
Function f = new Function(prefix, localName, m);
functions.put(key, f);
}
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.functions);
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
@SuppressWarnings("unchecked")
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.functions = (ConcurrentMap<String, Function>) in.readObject();
}
public static class Function implements Externalizable {
protected transient Method m;
protected String owner;
protected String name;
protected String[] types;
protected String prefix;
protected String localName;
public Function(String prefix, String localName, Method m) {
if (localName == null) {
throw new NullPointerException("LocalName cannot be null");
}
if (m == null) {
throw new NullPointerException("Method cannot be null");
}
this.prefix = prefix;
this.localName = localName;
this.m = m;
}
public Function() {
// for serialization
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF((this.prefix != null) ? this.prefix : "");
out.writeUTF(this.localName);
// make sure m isn't null
getMethod();
out.writeUTF((this.owner != null) ?
this.owner :
this.m.getDeclaringClass().getName());
out.writeUTF((this.name != null) ?
this.name :
this.m.getName());
out.writeObject((this.types != null) ?
this.types :
ReflectionUtil.toTypeNameArray(this.m.getParameterTypes()));
}
/*
* (non-Javadoc)
*
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.prefix = in.readUTF();
if ("".equals(this.prefix)) this.prefix = null;
this.localName = in.readUTF();
this.owner = in.readUTF();
this.name = in.readUTF();
this.types = (String[]) in.readObject();
}
public Method getMethod() {
if (this.m == null) {
try {
Class<?> t = ReflectionUtil.forName(this.owner);
Class<?>[] p = ReflectionUtil.toTypeArray(this.types);
this.m = t.getMethod(this.name, p);
} catch (Exception e) {
e.printStackTrace();
}
}
return this.m;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Function) {
return this.hashCode() == obj.hashCode();
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return (this.prefix + this.localName).hashCode();
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import javax.el.ValueExpression;
import javax.el.VariableMapper;
public class VariableMapperFactory extends VariableMapper {
private final VariableMapper target;
private VariableMapper momento;
public VariableMapperFactory(VariableMapper target) {
if (target == null) {
throw new NullPointerException("Target VariableMapper cannot be null");
}
this.target = target;
}
public VariableMapper create() {
return this.momento;
}
@Override
public ValueExpression resolveVariable(String variable) {
ValueExpression expr = this.target.resolveVariable(variable);
if (expr != null) {
if (this.momento == null) {
this.momento = new VariableMapperImpl();
}
this.momento.setVariable(variable, expr);
}
return expr;
}
@Override
public ValueExpression setVariable(String variable, ValueExpression expression) {
throw new UnsupportedOperationException("Cannot Set Variables on Factory");
}
}

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.el.lang;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.Map;
import javax.el.ValueExpression;
import javax.el.VariableMapper;
public class VariableMapperImpl extends VariableMapper implements Externalizable {
private static final long serialVersionUID = 1L;
private Map<String, ValueExpression> vars = new HashMap<>();
public VariableMapperImpl() {
super();
}
@Override
public ValueExpression resolveVariable(String variable) {
return this.vars.get(variable);
}
@Override
public ValueExpression setVariable(String variable,
ValueExpression expression) {
if (expression == null) {
return vars.remove(variable);
} else {
return vars.put(variable, expression);
}
}
@SuppressWarnings("unchecked")
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.vars = (Map<String, ValueExpression>) in.readObject();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.vars);
}
}