/*
 * 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 jakarta.el;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

Since:EL 3.0
/** * @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.canAccess(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.
Params:
  • prefix – Function prefix
  • function – Function name
  • method – Method
Throws:
/** * 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.canAccess(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; }
Returns:null 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
/** * @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; } } }