363 lines
14 KiB
Java
363 lines
14 KiB
Java
/*
|
|
* 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 javax.el;
|
|
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* @since EL 3.0
|
|
*/
|
|
public class ELProcessor {
|
|
|
|
private static final Set<String> PRIMITIVES = new HashSet<>();
|
|
static {
|
|
PRIMITIVES.add("boolean");
|
|
PRIMITIVES.add("byte");
|
|
PRIMITIVES.add("char");
|
|
PRIMITIVES.add("double");
|
|
PRIMITIVES.add("float");
|
|
PRIMITIVES.add("int");
|
|
PRIMITIVES.add("long");
|
|
PRIMITIVES.add("short");
|
|
}
|
|
|
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
|
|
private final ELManager manager = new ELManager();
|
|
private final ELContext context = manager.getELContext();
|
|
private final ExpressionFactory factory = ELManager.getExpressionFactory();
|
|
|
|
|
|
public ELManager getELManager() {
|
|
return manager;
|
|
}
|
|
|
|
|
|
public Object eval(String expression) {
|
|
return getValue(expression, Object.class);
|
|
}
|
|
|
|
|
|
public Object getValue(String expression, Class<?> expectedType) {
|
|
ValueExpression ve = factory.createValueExpression(
|
|
context, bracket(expression), expectedType);
|
|
return ve.getValue(context);
|
|
}
|
|
|
|
|
|
public void setValue(String expression, Object value) {
|
|
ValueExpression ve = factory.createValueExpression(
|
|
context, bracket(expression), Object.class);
|
|
ve.setValue(context, value);
|
|
}
|
|
|
|
|
|
public void setVariable(String variable, String expression) {
|
|
if (expression == null) {
|
|
manager.setVariable(variable, null);
|
|
} else {
|
|
ValueExpression ve = factory.createValueExpression(
|
|
context, bracket(expression), Object.class);
|
|
manager.setVariable(variable, ve);
|
|
}
|
|
}
|
|
|
|
|
|
public void defineFunction(String prefix, String function, String className,
|
|
String methodName) throws ClassNotFoundException,
|
|
NoSuchMethodException {
|
|
|
|
if (prefix == null || function == null || className == null ||
|
|
methodName == null) {
|
|
throw new NullPointerException(Util.message(
|
|
context, "elProcessor.defineFunctionNullParams"));
|
|
}
|
|
|
|
// Check the imports
|
|
Class<?> clazz = context.getImportHandler().resolveClass(className);
|
|
|
|
if (clazz == null) {
|
|
clazz = Class.forName(className, true, Util.getContextClassLoader());
|
|
}
|
|
|
|
if (!Modifier.isPublic(clazz.getModifiers())) {
|
|
throw new ClassNotFoundException(Util.message(context,
|
|
"elProcessor.defineFunctionInvalidClass", className));
|
|
}
|
|
|
|
MethodSignature sig =
|
|
new MethodSignature(context, methodName, className);
|
|
|
|
if (function.length() == 0) {
|
|
function = sig.getName();
|
|
}
|
|
|
|
// Only returns public methods. Java 9+ access is checked below.
|
|
Method methods[] = clazz.getMethods();
|
|
JreCompat jreCompat = JreCompat.getInstance();
|
|
|
|
for (Method method : methods) {
|
|
if (!Modifier.isStatic(method.getModifiers())) {
|
|
continue;
|
|
}
|
|
if (!jreCompat.canAcccess(null, method)) {
|
|
continue;
|
|
}
|
|
if (method.getName().equals(sig.getName())) {
|
|
if (sig.getParamTypeNames() == null) {
|
|
// Only a name provided, no signature so map the first
|
|
// method declared
|
|
manager.mapFunction(prefix, function, method);
|
|
return;
|
|
}
|
|
if (sig.getParamTypeNames().length != method.getParameterTypes().length) {
|
|
continue;
|
|
}
|
|
if (sig.getParamTypeNames().length == 0) {
|
|
manager.mapFunction(prefix, function, method);
|
|
return;
|
|
} else {
|
|
Class<?>[] types = method.getParameterTypes();
|
|
String[] typeNames = sig.getParamTypeNames();
|
|
if (types.length == typeNames.length) {
|
|
boolean match = true;
|
|
for (int i = 0; i < types.length; i++) {
|
|
if (i == types.length -1 && method.isVarArgs()) {
|
|
String typeName = typeNames[i];
|
|
if (typeName.endsWith("...")) {
|
|
typeName = typeName.substring(0, typeName.length() - 3);
|
|
if (!typeName.equals(types[i].getName())) {
|
|
match = false;
|
|
}
|
|
} else {
|
|
match = false;
|
|
}
|
|
} else if (!types[i].getName().equals(typeNames[i])) {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
if (match) {
|
|
manager.mapFunction(prefix, function, method);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new NoSuchMethodException(Util.message(context,
|
|
"elProcessor.defineFunctionNoMethod", methodName, className));
|
|
}
|
|
|
|
|
|
/**
|
|
* Map a method to a function name.
|
|
*
|
|
* @param prefix Function prefix
|
|
* @param function Function name
|
|
* @param method Method
|
|
*
|
|
* @throws NullPointerException
|
|
* If any of the arguments are null
|
|
* @throws NoSuchMethodException
|
|
* If the method is not static
|
|
*/
|
|
public void defineFunction(String prefix, String function, Method method)
|
|
throws java.lang.NoSuchMethodException {
|
|
|
|
if (prefix == null || function == null || method == null) {
|
|
throw new NullPointerException(Util.message(
|
|
context, "elProcessor.defineFunctionNullParams"));
|
|
}
|
|
|
|
int modifiers = method.getModifiers();
|
|
|
|
// Check for static, public method and module access for Java 9+
|
|
JreCompat jreCompat = JreCompat.getInstance();
|
|
if (!Modifier.isStatic(modifiers) || !jreCompat.canAcccess(null, method)) {
|
|
throw new NoSuchMethodException(Util.message(context,
|
|
"elProcessor.defineFunctionInvalidMethod", method.getName(),
|
|
method.getDeclaringClass().getName()));
|
|
}
|
|
|
|
manager.mapFunction(prefix, function, method);
|
|
}
|
|
|
|
|
|
public void defineBean(String name, Object bean) {
|
|
manager.defineBean(name, bean);
|
|
}
|
|
|
|
|
|
private static String bracket(String expression) {
|
|
return "${" + expression + "}";
|
|
}
|
|
|
|
private static class MethodSignature {
|
|
|
|
private final String name;
|
|
private final String[] parameterTypeNames;
|
|
|
|
public MethodSignature(ELContext context, String methodName,
|
|
String className) throws NoSuchMethodException {
|
|
|
|
int paramIndex = methodName.indexOf('(');
|
|
|
|
if (paramIndex == -1) {
|
|
name = methodName.trim();
|
|
parameterTypeNames = null;
|
|
} else {
|
|
String returnTypeAndName = methodName.substring(0, paramIndex).trim();
|
|
// Assume that the return type and the name are separated by
|
|
// whitespace. Given the use of trim() above, there should only
|
|
// be one sequence of whitespace characters.
|
|
int wsPos = -1;
|
|
for (int i = 0; i < returnTypeAndName.length(); i++) {
|
|
if (Character.isWhitespace(returnTypeAndName.charAt(i))) {
|
|
wsPos = i;
|
|
break;
|
|
}
|
|
}
|
|
if (wsPos == -1) {
|
|
throw new NoSuchMethodException();
|
|
}
|
|
name = returnTypeAndName.substring(wsPos).trim();
|
|
|
|
String paramString = methodName.substring(paramIndex).trim();
|
|
// We know the params start with '(', check they end with ')'
|
|
if (!paramString.endsWith(")")) {
|
|
throw new NoSuchMethodException(Util.message(context,
|
|
"elProcessor.defineFunctionInvalidParameterList",
|
|
paramString, methodName, className));
|
|
}
|
|
// Trim '(' and ')'
|
|
paramString = paramString.substring(1, paramString.length() - 1).trim();
|
|
if (paramString.length() == 0) {
|
|
parameterTypeNames = EMPTY_STRING_ARRAY;
|
|
} else {
|
|
parameterTypeNames = paramString.split(",");
|
|
ImportHandler importHandler = context.getImportHandler();
|
|
for (int i = 0; i < parameterTypeNames.length; i++) {
|
|
String parameterTypeName = parameterTypeNames[i].trim();
|
|
int dimension = 0;
|
|
int bracketPos = parameterTypeName.indexOf('[');
|
|
if (bracketPos > -1) {
|
|
String parameterTypeNameOnly =
|
|
parameterTypeName.substring(0, bracketPos).trim();
|
|
while (bracketPos > -1) {
|
|
dimension++;
|
|
bracketPos = parameterTypeName.indexOf('[', bracketPos+ 1);
|
|
}
|
|
parameterTypeName = parameterTypeNameOnly;
|
|
}
|
|
boolean varArgs = false;
|
|
if (parameterTypeName.endsWith("...")) {
|
|
varArgs = true;
|
|
dimension = 1;
|
|
parameterTypeName = parameterTypeName.substring(
|
|
0, parameterTypeName.length() -3).trim();
|
|
}
|
|
boolean isPrimitive = PRIMITIVES.contains(parameterTypeName);
|
|
if (isPrimitive && dimension > 0) {
|
|
// When in an array, class name changes for primitive
|
|
switch(parameterTypeName)
|
|
{
|
|
case "boolean":
|
|
parameterTypeName = "Z";
|
|
break;
|
|
case "byte":
|
|
parameterTypeName = "B";
|
|
break;
|
|
case "char":
|
|
parameterTypeName = "C";
|
|
break;
|
|
case "double":
|
|
parameterTypeName = "D";
|
|
break;
|
|
case "float":
|
|
parameterTypeName = "F";
|
|
break;
|
|
case "int":
|
|
parameterTypeName = "I";
|
|
break;
|
|
case "long":
|
|
parameterTypeName = "J";
|
|
break;
|
|
case "short":
|
|
parameterTypeName = "S";
|
|
break;
|
|
default:
|
|
// Should never happen
|
|
break;
|
|
}
|
|
} else if (!isPrimitive &&
|
|
!parameterTypeName.contains(".")) {
|
|
Class<?> clazz = importHandler.resolveClass(
|
|
parameterTypeName);
|
|
if (clazz == null) {
|
|
throw new NoSuchMethodException(Util.message(
|
|
context,
|
|
"elProcessor.defineFunctionInvalidParameterTypeName",
|
|
parameterTypeNames[i], methodName,
|
|
className));
|
|
}
|
|
parameterTypeName = clazz.getName();
|
|
}
|
|
if (dimension > 0) {
|
|
// Convert to array form of class name
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int j = 0; j < dimension; j++) {
|
|
sb.append('[');
|
|
}
|
|
if (!isPrimitive) {
|
|
sb.append('L');
|
|
}
|
|
sb.append(parameterTypeName);
|
|
if (!isPrimitive) {
|
|
sb.append(';');
|
|
}
|
|
parameterTypeName = sb.toString();
|
|
}
|
|
if (varArgs) {
|
|
parameterTypeName += "...";
|
|
}
|
|
parameterTypeNames[i] = parameterTypeName;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* @return <code>null</code> if just the method name was specified, an
|
|
* empty List if an empty parameter list was specified - i.e. ()
|
|
* - otherwise an ordered list of parameter type names
|
|
*/
|
|
public String[] getParamTypeNames() {
|
|
return parameterTypeNames;
|
|
}
|
|
}
|
|
}
|