ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/el/util/ReflectionUtil.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/el/util/ReflectionUtil.java
Status
scanned
Type
file
Name
ReflectionUtil.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
21850 (21.3 KB)
MD5
91ab622427794324285f999472f667f2
SHA1
c5cf57e2d711950f92415d11d4e003969d828eca
SHA256
5717f2d18e28597f1799503174ffc0dac663fccefa4c54a8fd108b05cd202814
SHA512

      
    
SHA1_git
1f8f07297be997a81285cf96dd13e68a585353c2
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
ReflectionUtil.java | 21.3 KB |

/* * 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.util; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import jakarta.el.ELException; import jakarta.el.MethodNotFoundException; import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; /** * Utilities for Managing Serialization and Reflection. */ public class ReflectionUtil { protected static final String[] PRIMITIVE_NAMES = new String[] { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" }; protected static final Class<?>[] PRIMITIVES = new Class[] { boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class, Void.TYPE }; private ReflectionUtil() { super(); } public static Class<?> forName(String name) throws ClassNotFoundException { if (null == name || name.isEmpty()) { return null; } Class<?> c = forNamePrimitive(name); if (c == null) { if (name.endsWith("[]")) { String nc = name.substring(0, name.length() - 2); c = Class.forName(nc, true, Thread.currentThread().getContextClassLoader()); c = Array.newInstance(c, 0).getClass(); } else { c = Class.forName(name, true, Thread.currentThread().getContextClassLoader()); } } return c; } protected static Class<?> forNamePrimitive(String name) { if (name.length() <= 8) { int p = Arrays.binarySearch(PRIMITIVE_NAMES, name); if (p >= 0) { return PRIMITIVES[p]; } } return null; } /** * Converts an array of Class names to Class types. * * @param s The array of class names * * @return An array of Class instance where the element at index i in the result is an instance of the class with * the name at index i in the input * * @throws ClassNotFoundException If a class of a given name cannot be found */ public static Class<?>[] toTypeArray(String[] s) throws ClassNotFoundException { if (s == null) { return null; } Class<?>[] c = new Class[s.length]; for (int i = 0; i < s.length; i++) { c[i] = forName(s[i]); } return c; } /** * Converts an array of Class types to Class names. * * @param c The array of class instances * * @return An array of Class names where the element at index i in the result is the name of the class instance at * index i in the input */ public static String[] toTypeNameArray(Class<?>[] c) { if (c == null) { return null; } String[] s = new String[c.length]; for (int i = 0; i < c.length; i++) { s[i] = c[i].getName(); } return s; } /** * Returns a method based on the criteria. * * @param ctx the context in which the expression is being evaluated * @param base the object that owns the method * @param property the name of the method * @param paramTypes the parameter types to use * @param paramValues the parameter values * * @return the method specified * * @throws MethodNotFoundException If a method cannot be found that matches the given criteria */ /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ @SuppressWarnings("null") public static Method getMethod(EvaluationContext ctx, Object base, Object property, Class<?>[] paramTypes, Object[] paramValues) throws MethodNotFoundException { if (base == null || property == null) { throw new MethodNotFoundException( MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes))); } String methodName = (property instanceof String) ? (String) property : property.toString(); int paramCount; if (paramTypes == null) { paramCount = 0; } else { paramCount = paramTypes.length; } Class<?> clazz = base.getClass(); // Fast path: when no arguments exist, there can only be one matching method and no need for coercion. if (paramCount == 0) { Method result = null; Throwable t = null; try { Method method = clazz.getMethod(methodName, paramTypes); result = getMethod(clazz, base, method); } catch (NoSuchMethodException | SecurityException e) { // Fall through t = e; } if (result == null) { throw new MethodNotFoundException( MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes)), t); } return result; } Method[] methods = clazz.getMethods(); Map<Method,MatchResult> candidates = new HashMap<>(); for (Method m : methods) { if (!m.getName().equals(methodName)) { // Method name doesn't match continue; } Class<?>[] mParamTypes = m.getParameterTypes(); int mParamCount = mParamTypes.length; // Check the number of parameters // Multiple tests to improve readability if (!m.isVarArgs() && paramCount != mParamCount) { // Method has wrong number of parameters continue; } if (m.isVarArgs() && paramCount < mParamCount - 1) { // Method has wrong number of parameters continue; } if (m.isVarArgs() && paramCount == mParamCount && paramValues != null && paramValues.length > paramCount && !paramTypes[mParamCount - 1].isArray()) { // Method arguments don't match continue; } if (m.isVarArgs() && paramCount > mParamCount && paramValues != null && paramValues.length != paramCount) { // Might match a different varargs method continue; } if (!m.isVarArgs() && paramValues != null && paramCount != paramValues.length) { // Might match a different varargs method continue; } // Check the parameters match int exactMatch = 0; int assignableMatch = 0; int coercibleMatch = 0; int varArgsMatch = 0; boolean noMatch = false; for (int i = 0; i < mParamCount; i++) { // Can't be null if (m.isVarArgs() && i == (mParamCount - 1)) { if (i == paramCount || (paramValues != null && paramValues.length == i)) { // Var args defined but nothing is passed as varargs // Use MAX_VALUE so this matches only if nothing else does varArgsMatch = Integer.MAX_VALUE; break; } Class<?> varType = mParamTypes[i].getComponentType(); for (int j = i; j < paramCount; j++) { if (isAssignableFrom(paramTypes[j], varType)) { assignableMatch++; varArgsMatch++; } else { if (paramValues == null) { noMatch = true; break; } else { if (isCoercibleFrom(ctx, paramValues[j], varType)) { coercibleMatch++; varArgsMatch++; } else { noMatch = true; break; } } } // Don't treat a varArgs match as an exact match, it can // lead to a varArgs method matching when the result // should be ambiguous } } else { if (mParamTypes[i].equals(paramTypes[i])) { exactMatch++; } else if (paramTypes[i] != null && isAssignableFrom(paramTypes[i], mParamTypes[i])) { assignableMatch++; } else { if (paramValues == null) { noMatch = true; break; } else { if (isCoercibleFrom(ctx, paramValues[i], mParamTypes[i])) { coercibleMatch++; } else { noMatch = true; break; } } } } } if (noMatch) { continue; } // If a method is found where every parameter matches exactly, // and no vars args are present, return it if (exactMatch == paramCount && varArgsMatch == 0) { Method result = getMethod(clazz, base, m); if (result == null) { throw new MethodNotFoundException( MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes))); } return result; } candidates.put(m, new MatchResult(m.isVarArgs(), exactMatch, assignableMatch, coercibleMatch, varArgsMatch, m.isBridge())); } // Look for the method that has the highest number of parameters where // the type matches exactly MatchResult bestMatch = new MatchResult(true, 0, 0, 0, 0, true); Method match = null; boolean multiple = false; for (Map.Entry<Method,MatchResult> entry : candidates.entrySet()) { int cmp = entry.getValue().compareTo(bestMatch); if (cmp > 0 || match == null) { bestMatch = entry.getValue(); match = entry.getKey(); multiple = false; } else if (cmp == 0) { multiple = true; } } if (multiple) { if (bestMatch.exactCount() == paramCount - 1) { // Only one parameter is not an exact match - try using the // super class match = resolveAmbiguousMethod(candidates.keySet(), paramTypes); } else { match = null; } if (match == null) { // If multiple methods have the same matching number of parameters // the match is ambiguous so throw an exception throw new MethodNotFoundException( MessageFactory.get("error.method.ambiguous", base, property, paramString(paramTypes))); } } // Handle case where no match at all was found if (match == null) { throw new MethodNotFoundException( MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes))); } Method result = getMethod(clazz, base, match); if (result == null) { throw new MethodNotFoundException( MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes))); } return result; } /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private static Method resolveAmbiguousMethod(Set<Method> candidates, Class<?>[] paramTypes) { // Identify which parameter isn't an exact match Method m = candidates.iterator().next(); int nonMatchIndex = 0; Class<?> nonMatchClass = null; for (int i = 0; i < paramTypes.length; i++) { if (m.getParameterTypes()[i] != paramTypes[i]) { nonMatchIndex = i; nonMatchClass = paramTypes[i]; break; } } if (nonMatchClass == null) { // Null will always be ambiguous return null; } for (Method c : candidates) { if (c.getParameterTypes()[nonMatchIndex] == paramTypes[nonMatchIndex]) { // Methods have different non-matching parameters // Result is ambiguous return null; } } // Can't be null Class<?> superClass = nonMatchClass.getSuperclass(); while (superClass != null) { for (Method c : candidates) { if (c.getParameterTypes()[nonMatchIndex].equals(superClass)) { // Found a match return c; } } superClass = superClass.getSuperclass(); } // Treat instances of Number as a special case Method match = null; if (Number.class.isAssignableFrom(nonMatchClass)) { for (Method c : candidates) { Class<?> candidateType = c.getParameterTypes()[nonMatchIndex]; if (Number.class.isAssignableFrom(candidateType) || candidateType.isPrimitive()) { if (match == null) { match = c; } else { // Match still ambiguous match = null; break; } } } } return match; } /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private static boolean isAssignableFrom(Class<?> src, Class<?> target) { // src will always be an object // Short-cut. null is always assignable to an object and in EL null // can always be coerced to a valid value for a primitive if (src == null) { return true; } Class<?> targetClass; if (target.isPrimitive()) { if (target == Boolean.TYPE) { targetClass = Boolean.class; } else if (target == Character.TYPE) { targetClass = Character.class; } else if (target == Byte.TYPE) { targetClass = Byte.class; } else if (target == Short.TYPE) { targetClass = Short.class; } else if (target == Integer.TYPE) { targetClass = Integer.class; } else if (target == Long.TYPE) { targetClass = Long.class; } else if (target == Float.TYPE) { targetClass = Float.class; } else { targetClass = Double.class; } } else { targetClass = target; } return targetClass.isAssignableFrom(src); } /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private static boolean isCoercibleFrom(EvaluationContext ctx, Object src, Class<?> target) { // TODO: This isn't pretty but it works. Significant refactoring would be required to avoid the exception. try { ELSupport.coerceToType(ctx, src, target); } catch (ELException e) { return false; } return true; } /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private static Method getMethod(Class<?> type, Object base, Method m) { if (m == null || (Modifier.isPublic(type.getModifiers()) && (Modifier.isStatic(m.getModifiers()) && m.canAccess(null) || m.canAccess(base)))) { return m; } Class<?>[] interfaces = type.getInterfaces(); Method mp; for (Class<?> iface : interfaces) { try { mp = iface.getMethod(m.getName(), m.getParameterTypes()); mp = getMethod(mp.getDeclaringClass(), base, mp); if (mp != null) { return mp; } } catch (NoSuchMethodException e) { // Ignore } } Class<?> sup = type.getSuperclass(); if (sup != null) { try { mp = sup.getMethod(m.getName(), m.getParameterTypes()); mp = getMethod(mp.getDeclaringClass(), base, mp); if (mp != null) { return mp; } } catch (NoSuchMethodException e) { // Ignore } } return null; } private static String paramString(Class<?>[] types) { if (types != null) { StringBuilder sb = new StringBuilder(); for (Class<?> type : types) { if (type == null) { sb.append("null, "); } else { sb.append(type.getName()).append(", "); } } if (sb.length() > 2) { sb.setLength(sb.length() - 2); } return sb.toString(); } return null; } /* * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private record MatchResult(boolean varArgs, int exactCount, int assignableCount, int coercibleCount, int varArgsCount, boolean bridge) implements Comparable<MatchResult> { @Override public int compareTo(MatchResult o) { // Non-varArgs always beats varArgs int cmp = Boolean.compare(o.varArgs(), this.varArgs()); if (cmp == 0) { cmp = Integer.compare(this.exactCount(), o.exactCount()); if (cmp == 0) { cmp = Integer.compare(this.assignableCount(), o.assignableCount()); if (cmp == 0) { cmp = Integer.compare(this.coercibleCount(), o.coercibleCount()); if (cmp == 0) { // Fewer var args matches are better cmp = Integer.compare(o.varArgsCount(), this.varArgsCount()); if (cmp == 0) { // The nature of bridge methods is such that it actually // doesn't matter which one we pick as long as we pick // one. That said, pick the 'right' one (the non-bridge // one) anyway. cmp = Boolean.compare(o.bridge(), this.bridge()); } } } } } return cmp; } @Override public boolean equals(Object o) { return o == this || (null != o && this.getClass().equals(o.getClass()) && ((MatchResult) o).exactCount() == this.exactCount() && ((MatchResult) o).assignableCount() == this.assignableCount() && ((MatchResult) o).coercibleCount() == this.coercibleCount() && ((MatchResult) o).varArgsCount() == this.varArgsCount() && ((MatchResult) o).varArgs() == this.varArgs() && ((MatchResult) o).bridge() == this.bridge()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + assignableCount(); result = prime * result + (bridge() ? 1231 : 1237); result = prime * result + coercibleCount(); result = prime * result + exactCount(); result = prime * result + (varArgs() ? 1231 : 1237); result = prime * result + varArgsCount(); return result; } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
6.39
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9