init
This commit is contained in:
401
java/org/apache/el/lang/ELArithmetic.java
Normal file
401
java/org/apache/el/lang/ELArithmetic.java
Normal 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);
|
||||
|
||||
}
|
||||
655
java/org/apache/el/lang/ELSupport.java
Normal file
655
java/org/apache/el/lang/ELSupport.java
Normal file
@@ -0,0 +1,655 @@
|
||||
/*
|
||||
* 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.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorManager;
|
||||
import java.lang.reflect.Array;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELException;
|
||||
|
||||
import org.apache.el.util.MessageFactory;
|
||||
|
||||
|
||||
/**
|
||||
* A helper class that implements the EL Specification
|
||||
*
|
||||
* @author Jacob Hookom [jacob@hookom.net]
|
||||
*/
|
||||
public class ELSupport {
|
||||
|
||||
private static final Long ZERO = Long.valueOf(0L);
|
||||
|
||||
protected static final boolean COERCE_TO_ZERO;
|
||||
|
||||
static {
|
||||
String coerceToZeroStr;
|
||||
if (System.getSecurityManager() != null) {
|
||||
coerceToZeroStr = AccessController.doPrivileged(
|
||||
new PrivilegedAction<String>(){
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty(
|
||||
"org.apache.el.parser.COERCE_TO_ZERO", "false");
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
coerceToZeroStr = System.getProperty(
|
||||
"org.apache.el.parser.COERCE_TO_ZERO", "false");
|
||||
}
|
||||
COERCE_TO_ZERO = Boolean.parseBoolean(coerceToZeroStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare two objects, after coercing to the same type if appropriate.
|
||||
*
|
||||
* If the objects are identical, or they are equal according to
|
||||
* {@link #equals(ELContext, Object, Object)} then return 0.
|
||||
*
|
||||
* If either object is a BigDecimal, then coerce both to BigDecimal first.
|
||||
* Similarly for Double(Float), BigInteger, and Long(Integer, Char, Short, Byte).
|
||||
*
|
||||
* Otherwise, check that the first object is an instance of Comparable, and compare
|
||||
* against the second object. If that is null, return 1, otherwise
|
||||
* return the result of comparing against the second object.
|
||||
*
|
||||
* Similarly, if the second object is Comparable, if the first is null, return -1,
|
||||
* else return the result of comparing against the first object.
|
||||
*
|
||||
* A null object is considered as:
|
||||
* <ul>
|
||||
* <li>ZERO when compared with Numbers</li>
|
||||
* <li>the empty string for String compares</li>
|
||||
* <li>Otherwise null is considered to be lower than anything else.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx the context in which this comparison is taking place
|
||||
* @param obj0 first object
|
||||
* @param obj1 second object
|
||||
* @return -1, 0, or 1 if this object is less than, equal to, or greater than val.
|
||||
* @throws ELException if neither object is Comparable
|
||||
* @throws ClassCastException if the objects are not mutually comparable
|
||||
*/
|
||||
public static final int compare(final ELContext ctx, final Object obj0, final Object obj1)
|
||||
throws ELException {
|
||||
if (obj0 == obj1 || equals(ctx, obj0, obj1)) {
|
||||
return 0;
|
||||
}
|
||||
if (isBigDecimalOp(obj0, obj1)) {
|
||||
BigDecimal bd0 = (BigDecimal) coerceToNumber(ctx, obj0, BigDecimal.class);
|
||||
BigDecimal bd1 = (BigDecimal) coerceToNumber(ctx, obj1, BigDecimal.class);
|
||||
return bd0.compareTo(bd1);
|
||||
}
|
||||
if (isDoubleOp(obj0, obj1)) {
|
||||
Double d0 = (Double) coerceToNumber(ctx, obj0, Double.class);
|
||||
Double d1 = (Double) coerceToNumber(ctx, obj1, Double.class);
|
||||
return d0.compareTo(d1);
|
||||
}
|
||||
if (isBigIntegerOp(obj0, obj1)) {
|
||||
BigInteger bi0 = (BigInteger) coerceToNumber(ctx, obj0, BigInteger.class);
|
||||
BigInteger bi1 = (BigInteger) coerceToNumber(ctx, obj1, BigInteger.class);
|
||||
return bi0.compareTo(bi1);
|
||||
}
|
||||
if (isLongOp(obj0, obj1)) {
|
||||
Long l0 = (Long) coerceToNumber(ctx, obj0, Long.class);
|
||||
Long l1 = (Long) coerceToNumber(ctx, obj1, Long.class);
|
||||
return l0.compareTo(l1);
|
||||
}
|
||||
if (obj0 instanceof String || obj1 instanceof String) {
|
||||
return coerceToString(ctx, obj0).compareTo(coerceToString(ctx, obj1));
|
||||
}
|
||||
if (obj0 instanceof Comparable<?>) {
|
||||
@SuppressWarnings("unchecked") // checked above
|
||||
final Comparable<Object> comparable = (Comparable<Object>) obj0;
|
||||
return (obj1 != null) ? comparable.compareTo(obj1) : 1;
|
||||
}
|
||||
if (obj1 instanceof Comparable<?>) {
|
||||
@SuppressWarnings("unchecked") // checked above
|
||||
final Comparable<Object> comparable = (Comparable<Object>) obj1;
|
||||
return (obj0 != null) ? -comparable.compareTo(obj0) : -1;
|
||||
}
|
||||
throw new ELException(MessageFactory.get("error.compare", obj0, obj1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two objects for equality, after coercing to the same type if appropriate.
|
||||
*
|
||||
* If the objects are identical (including both null) return true.
|
||||
* If either object is null, return false.
|
||||
* If either object is Boolean, coerce both to Boolean and check equality.
|
||||
* Similarly for Enum, String, BigDecimal, Double(Float), Long(Integer, Short, Byte, Character)
|
||||
* Otherwise default to using Object.equals().
|
||||
*
|
||||
* @param ctx the context in which this equality test is taking place
|
||||
* @param obj0 the first object
|
||||
* @param obj1 the second object
|
||||
* @return true if the objects are equal
|
||||
* @throws ELException if one of the coercion fails
|
||||
*/
|
||||
public static final boolean equals(final ELContext ctx, final Object obj0, final Object obj1)
|
||||
throws ELException {
|
||||
if (obj0 == obj1) {
|
||||
return true;
|
||||
} else if (obj0 == null || obj1 == null) {
|
||||
return false;
|
||||
} else if (isBigDecimalOp(obj0, obj1)) {
|
||||
BigDecimal bd0 = (BigDecimal) coerceToNumber(ctx, obj0, BigDecimal.class);
|
||||
BigDecimal bd1 = (BigDecimal) coerceToNumber(ctx, obj1, BigDecimal.class);
|
||||
return bd0.equals(bd1);
|
||||
} else if (isDoubleOp(obj0, obj1)) {
|
||||
Double d0 = (Double) coerceToNumber(ctx, obj0, Double.class);
|
||||
Double d1 = (Double) coerceToNumber(ctx, obj1, Double.class);
|
||||
return d0.equals(d1);
|
||||
} else if (isBigIntegerOp(obj0, obj1)) {
|
||||
BigInteger bi0 = (BigInteger) coerceToNumber(ctx, obj0, BigInteger.class);
|
||||
BigInteger bi1 = (BigInteger) coerceToNumber(ctx, obj1, BigInteger.class);
|
||||
return bi0.equals(bi1);
|
||||
} else if (isLongOp(obj0, obj1)) {
|
||||
Long l0 = (Long) coerceToNumber(ctx, obj0, Long.class);
|
||||
Long l1 = (Long) coerceToNumber(ctx, obj1, Long.class);
|
||||
return l0.equals(l1);
|
||||
} else if (obj0 instanceof Boolean || obj1 instanceof Boolean) {
|
||||
return coerceToBoolean(ctx, obj0, false).equals(coerceToBoolean(ctx, obj1, false));
|
||||
} else if (obj0.getClass().isEnum()) {
|
||||
return obj0.equals(coerceToEnum(ctx, obj1, obj0.getClass()));
|
||||
} else if (obj1.getClass().isEnum()) {
|
||||
return obj1.equals(coerceToEnum(ctx, obj0, obj1.getClass()));
|
||||
} else if (obj0 instanceof String || obj1 instanceof String) {
|
||||
int lexCompare = coerceToString(ctx, obj0).compareTo(coerceToString(ctx, obj1));
|
||||
return (lexCompare == 0) ? true : false;
|
||||
} else {
|
||||
return obj0.equals(obj1);
|
||||
}
|
||||
}
|
||||
|
||||
// Going to have to have some casts /raw types somewhere so doing it here
|
||||
// keeps them all in one place. There might be a neater / better solution
|
||||
// but I couldn't find it
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Enum<?> coerceToEnum(final ELContext ctx, final Object obj,
|
||||
@SuppressWarnings("rawtypes") Class type) {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, type);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return (Enum<?>) result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == null || "".equals(obj)) {
|
||||
return null;
|
||||
}
|
||||
if (type.isAssignableFrom(obj.getClass())) {
|
||||
return (Enum<?>) obj;
|
||||
}
|
||||
|
||||
if (!(obj instanceof String)) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), type));
|
||||
}
|
||||
|
||||
Enum<?> result;
|
||||
try {
|
||||
result = Enum.valueOf(type, (String) obj);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), type));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an object to Boolean.
|
||||
* Null and empty string are false.
|
||||
* @param ctx the context in which this conversion is taking place
|
||||
* @param obj the object to convert
|
||||
* @param primitive is the target a primitive in which case coercion to null
|
||||
* is not permitted
|
||||
* @return the Boolean value of the object
|
||||
* @throws ELException if object is not Boolean or String
|
||||
*/
|
||||
public static final Boolean coerceToBoolean(final ELContext ctx, final Object obj,
|
||||
boolean primitive) throws ELException {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, Boolean.class);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return (Boolean) result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (!COERCE_TO_ZERO && !primitive) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == null || "".equals(obj)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if (obj instanceof Boolean) {
|
||||
return (Boolean) obj;
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
return Boolean.valueOf((String) obj);
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), Boolean.class));
|
||||
}
|
||||
|
||||
private static final Character coerceToCharacter(final ELContext ctx, final Object obj)
|
||||
throws ELException {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, Character.class);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return (Character) result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == null || "".equals(obj)) {
|
||||
return Character.valueOf((char) 0);
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
return Character.valueOf(((String) obj).charAt(0));
|
||||
}
|
||||
if (ELArithmetic.isNumber(obj)) {
|
||||
return Character.valueOf((char) ((Number) obj).shortValue());
|
||||
}
|
||||
Class<?> objType = obj.getClass();
|
||||
if (obj instanceof Character) {
|
||||
return (Character) obj;
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, objType, Character.class));
|
||||
}
|
||||
|
||||
protected static final Number coerceToNumber(final Number number,
|
||||
final Class<?> type) throws ELException {
|
||||
if (Long.TYPE == type || Long.class.equals(type)) {
|
||||
return Long.valueOf(number.longValue());
|
||||
}
|
||||
if (Double.TYPE == type || Double.class.equals(type)) {
|
||||
return Double.valueOf(number.doubleValue());
|
||||
}
|
||||
if (Integer.TYPE == type || Integer.class.equals(type)) {
|
||||
return Integer.valueOf(number.intValue());
|
||||
}
|
||||
if (BigInteger.class.equals(type)) {
|
||||
if (number instanceof BigDecimal) {
|
||||
return ((BigDecimal) number).toBigInteger();
|
||||
}
|
||||
if (number instanceof BigInteger) {
|
||||
return number;
|
||||
}
|
||||
return BigInteger.valueOf(number.longValue());
|
||||
}
|
||||
if (BigDecimal.class.equals(type)) {
|
||||
if (number instanceof BigDecimal) {
|
||||
return number;
|
||||
}
|
||||
if (number instanceof BigInteger) {
|
||||
return new BigDecimal((BigInteger) number);
|
||||
}
|
||||
return new BigDecimal(number.doubleValue());
|
||||
}
|
||||
if (Byte.TYPE == type || Byte.class.equals(type)) {
|
||||
return Byte.valueOf(number.byteValue());
|
||||
}
|
||||
if (Short.TYPE == type || Short.class.equals(type)) {
|
||||
return Short.valueOf(number.shortValue());
|
||||
}
|
||||
if (Float.TYPE == type || Float.class.equals(type)) {
|
||||
return Float.valueOf(number.floatValue());
|
||||
}
|
||||
if (Number.class.equals(type)) {
|
||||
return number;
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
number, number.getClass(), type));
|
||||
}
|
||||
|
||||
public static final Number coerceToNumber(final ELContext ctx, final Object obj,
|
||||
final Class<?> type) throws ELException {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, type);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return (Number) result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (!COERCE_TO_ZERO) {
|
||||
if (obj == null && !type.isPrimitive()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == null || "".equals(obj)) {
|
||||
return coerceToNumber(ZERO, type);
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
return coerceToNumber((String) obj, type);
|
||||
}
|
||||
if (ELArithmetic.isNumber(obj)) {
|
||||
return coerceToNumber((Number) obj, type);
|
||||
}
|
||||
|
||||
if (obj instanceof Character) {
|
||||
return coerceToNumber(Short.valueOf((short) ((Character) obj)
|
||||
.charValue()), type);
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), type));
|
||||
}
|
||||
|
||||
protected static final Number coerceToNumber(final String val,
|
||||
final Class<?> type) throws ELException {
|
||||
if (Long.TYPE == type || Long.class.equals(type)) {
|
||||
try {
|
||||
return Long.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (Integer.TYPE == type || Integer.class.equals(type)) {
|
||||
try {
|
||||
return Integer.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (Double.TYPE == type || Double.class.equals(type)) {
|
||||
try {
|
||||
return Double.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (BigInteger.class.equals(type)) {
|
||||
try {
|
||||
return new BigInteger(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (BigDecimal.class.equals(type)) {
|
||||
try {
|
||||
return new BigDecimal(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (Byte.TYPE == type || Byte.class.equals(type)) {
|
||||
try {
|
||||
return Byte.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (Short.TYPE == type || Short.class.equals(type)) {
|
||||
try {
|
||||
return Short.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
if (Float.TYPE == type || Float.class.equals(type)) {
|
||||
try {
|
||||
return Float.valueOf(val);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
val, String.class, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce an object to a string.
|
||||
* @param ctx the context in which this conversion is taking place
|
||||
* @param obj the object to convert
|
||||
* @return the String value of the object
|
||||
*/
|
||||
public static final String coerceToString(final ELContext ctx, final Object obj) {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, String.class);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return (String) result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == null) {
|
||||
return "";
|
||||
} else if (obj instanceof String) {
|
||||
return (String) obj;
|
||||
} else if (obj instanceof Enum<?>) {
|
||||
return ((Enum<?>) obj).name();
|
||||
} else {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Object coerceToType(final ELContext ctx, final Object obj,
|
||||
final Class<?> type) throws ELException {
|
||||
|
||||
if (ctx != null) {
|
||||
boolean originalIsPropertyResolved = ctx.isPropertyResolved();
|
||||
try {
|
||||
Object result = ctx.getELResolver().convertToType(ctx, obj, type);
|
||||
if (ctx.isPropertyResolved()) {
|
||||
return result;
|
||||
}
|
||||
} finally {
|
||||
ctx.setPropertyResolved(originalIsPropertyResolved);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null || Object.class.equals(type) ||
|
||||
(obj != null && type.isAssignableFrom(obj.getClass()))) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!COERCE_TO_ZERO) {
|
||||
if (obj == null && !type.isPrimitive() &&
|
||||
!String.class.isAssignableFrom(type)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.class.equals(type)) {
|
||||
return coerceToString(ctx, obj);
|
||||
}
|
||||
if (ELArithmetic.isNumberType(type)) {
|
||||
return coerceToNumber(ctx, obj, type);
|
||||
}
|
||||
if (Character.class.equals(type) || Character.TYPE == type) {
|
||||
return coerceToCharacter(ctx, obj);
|
||||
}
|
||||
if (Boolean.class.equals(type) || Boolean.TYPE == type) {
|
||||
return coerceToBoolean(ctx, obj, Boolean.TYPE == type);
|
||||
}
|
||||
if (type.isEnum()) {
|
||||
return coerceToEnum(ctx, obj, type);
|
||||
}
|
||||
|
||||
// new to spec
|
||||
if (obj == null)
|
||||
return null;
|
||||
if (obj instanceof String) {
|
||||
PropertyEditor editor = PropertyEditorManager.findEditor(type);
|
||||
if (editor == null) {
|
||||
if ("".equals(obj)) {
|
||||
return null;
|
||||
}
|
||||
throw new ELException(MessageFactory.get("error.convert", obj,
|
||||
obj.getClass(), type));
|
||||
} else {
|
||||
try {
|
||||
editor.setAsText((String) obj);
|
||||
return editor.getValue();
|
||||
} catch (RuntimeException e) {
|
||||
if ("".equals(obj)) {
|
||||
return null;
|
||||
}
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), type), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle special case because the syntax for the empty set is the same
|
||||
// for an empty map. The parser will always parse {} as an empty set.
|
||||
if (obj instanceof Set && type == Map.class &&
|
||||
((Set<?>) obj).isEmpty()) {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (type.isArray() && obj.getClass().isArray()) {
|
||||
return coerceToArray(ctx, obj, type);
|
||||
}
|
||||
|
||||
throw new ELException(MessageFactory.get("error.convert",
|
||||
obj, obj.getClass(), type));
|
||||
}
|
||||
|
||||
private static Object coerceToArray(final ELContext ctx, final Object obj,
|
||||
final Class<?> type) {
|
||||
// Note: Nested arrays will result in nested calls to this method.
|
||||
|
||||
// Note: Calling method has checked the obj is an array.
|
||||
|
||||
int size = Array.getLength(obj);
|
||||
// Cast the input object to an array (calling method has checked it is
|
||||
// an array)
|
||||
// Get the target type for the array elements
|
||||
Class<?> componentType = type.getComponentType();
|
||||
// Create a new array of the correct type
|
||||
Object result = Array.newInstance(componentType, size);
|
||||
// Coerce each element in turn.
|
||||
for (int i = 0; i < size; i++) {
|
||||
Array.set(result, i, coerceToType(ctx, Array.get(obj, i), componentType));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final boolean isBigDecimalOp(final Object obj0,
|
||||
final Object obj1) {
|
||||
return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
|
||||
}
|
||||
|
||||
public static final boolean isBigIntegerOp(final Object obj0,
|
||||
final Object obj1) {
|
||||
return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
|
||||
}
|
||||
|
||||
public static final boolean isDoubleOp(final Object obj0, final Object obj1) {
|
||||
return (obj0 instanceof Double
|
||||
|| obj1 instanceof Double
|
||||
|| obj0 instanceof Float
|
||||
|| obj1 instanceof Float);
|
||||
}
|
||||
|
||||
public static final boolean isLongOp(final Object obj0, final Object obj1) {
|
||||
return (obj0 instanceof Long
|
||||
|| obj1 instanceof Long
|
||||
|| obj0 instanceof Integer
|
||||
|| obj1 instanceof Integer
|
||||
|| obj0 instanceof Character
|
||||
|| obj1 instanceof Character
|
||||
|| obj0 instanceof Short
|
||||
|| obj1 instanceof Short
|
||||
|| obj0 instanceof Byte
|
||||
|| obj1 instanceof Byte);
|
||||
}
|
||||
|
||||
public static final boolean isStringFloat(final String str) {
|
||||
int len = str.length();
|
||||
if (len > 1) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
switch (str.charAt(i)) {
|
||||
case 'E':
|
||||
return true;
|
||||
case 'e':
|
||||
return true;
|
||||
case '.':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ELSupport() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
157
java/org/apache/el/lang/EvaluationContext.java
Normal file
157
java/org/apache/el/lang/EvaluationContext.java
Normal 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);
|
||||
}
|
||||
}
|
||||
337
java/org/apache/el/lang/ExpressionBuilder.java
Normal file
337
java/org/apache/el/lang/ExpressionBuilder.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
69
java/org/apache/el/lang/FunctionMapperFactory.java
Normal file
69
java/org/apache/el/lang/FunctionMapperFactory.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
188
java/org/apache/el/lang/FunctionMapperImpl.java
Normal file
188
java/org/apache/el/lang/FunctionMapperImpl.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
55
java/org/apache/el/lang/VariableMapperFactory.java
Normal file
55
java/org/apache/el/lang/VariableMapperFactory.java
Normal 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");
|
||||
}
|
||||
}
|
||||
66
java/org/apache/el/lang/VariableMapperImpl.java
Normal file
66
java/org/apache/el/lang/VariableMapperImpl.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user