
com.sun.el.util.ReflectionUtil Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.el.util;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.MethodNotFoundException;
import javax.el.PropertyNotFoundException;
import com.sun.el.lang.ELSupport;
/**
* Utilities for Managing Serialization and Reflection
*
* @author Jacob Hookom [[email protected]]
* @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
*/
public class ReflectionUtil {
protected static final String[] EMPTY_STRING = new String[0];
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 || "".equals(name)) {
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
* @return The array of Classes
* @throws ClassNotFoundException
*/
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
* @return The array of Classes
*/
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;
}
/**
* @param base The base object
* @param property The property
* @return The PropertyDescriptor for the base with the given property
* @throws ELException
* @throws PropertyNotFoundException
*/
public static PropertyDescriptor getPropertyDescriptor(Object base,
Object property) throws ELException, PropertyNotFoundException {
String name = ELSupport.coerceToString(property);
PropertyDescriptor p = null;
try {
PropertyDescriptor[] desc = Introspector.getBeanInfo(
base.getClass()).getPropertyDescriptors();
for (int i = 0; i < desc.length; i++) {
if (desc[i].getName().equals(name)) {
return desc[i];
}
}
} catch (IntrospectionException ie) {
throw new ELException(ie);
}
throw new PropertyNotFoundException(MessageFactory.get(
"error.property.notfound", base, name));
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
public static Object invokeMethod(ELContext context,
Method m, Object base, Object[] params) {
Object[] parameters = buildParameters(
context, m.getParameterTypes(), m.isVarArgs(), params);
try {
return m.invoke(base, parameters);
} catch (IllegalAccessException iae) {
throw new ELException(iae);
} catch (IllegalArgumentException iae) {
throw new ELException(iae);
} catch (InvocationTargetException ite) {
throw new ELException(ite.getCause());
}
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
public static Method findMethod(Class> clazz, String methodName,
Class>[] paramTypes, Object[] paramValues) {
if (clazz == null || methodName == null) {
throw new MethodNotFoundException(MessageFactory.get(
"error.method.notfound", clazz, methodName, paramString(paramTypes)));
}
if (paramTypes == null) {
paramTypes = getTypesFromValues(paramValues);
}
Method[] methods = clazz.getMethods();
List wrappers = Wrapper.wrap(methods, methodName);
Wrapper result = findWrapper(
clazz, wrappers, methodName, paramTypes, paramValues);
if (result == null) {
return null;
}
return getMethod(clazz, (Method) result.unWrap());
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static Wrapper findWrapper(Class> clazz, List wrappers,
String name, Class>[] paramTypes, Object[] paramValues) {
List assignableCandidates = new ArrayList();
List coercibleCandidates = new ArrayList();
List varArgsCandidates = new ArrayList();
int paramCount;
if (paramTypes == null) {
paramCount = 0;
} else {
paramCount = paramTypes.length;
}
for (Wrapper w : wrappers) {
Class>[] mParamTypes = w.getParameterTypes();
int mParamCount;
if (mParamTypes == null) {
mParamCount = 0;
} else {
mParamCount = mParamTypes.length;
}
// Check the number of parameters
if (!(paramCount == mParamCount ||
(w.isVarArgs() && paramCount >= mParamCount - 1))) {
// Method has wrong number of parameters
continue;
}
// Check the parameters match
boolean assignable = false;
boolean coercible = false;
boolean varArgs = false;
boolean noMatch = false;
for (int i = 0; i < mParamCount; i++) {
if (i == (mParamCount - 1) && w.isVarArgs()) {
varArgs = true;
// exact var array type match
if (mParamCount == paramCount) {
if (mParamTypes[i] == paramTypes[i]) {
continue;
}
}
// unwrap the array's component type
Class> varType = mParamTypes[i].getComponentType();
for (int j = i; j < paramCount; j++) {
if (!isAssignableFrom(paramTypes[j], varType)
&& !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) {
noMatch = true;
break;
}
}
} else if (mParamTypes[i].equals(paramTypes[i])) {
} else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) {
assignable = true;
} else {
if (paramValues == null || i >= paramValues.length) {
noMatch = true;
break;
} else {
if (isCoercibleFrom(paramValues[i], mParamTypes[i])) {
coercible = true;
} else {
noMatch = true;
break;
}
}
}
}
if (noMatch) {
continue;
}
if (varArgs) {
varArgsCandidates.add(w);
} else if (coercible) {
coercibleCandidates.add(w);
} else if (assignable) {
assignableCandidates.add(w);
} else {
// If a method is found where every parameter matches exactly,
// return it
return w;
}
}
String errorMsg = MessageFactory.get(
"error.method.ambiguous", clazz, name, paramString(paramTypes));
if (!assignableCandidates.isEmpty()) {
return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg);
} else if (!coercibleCandidates.isEmpty()) {
return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg);
} else if (!varArgsCandidates.isEmpty()) {
return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg);
} else {
throw new MethodNotFoundException(MessageFactory.get(
"error.method.notfound", clazz, name, paramString(paramTypes)));
}
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static Wrapper findMostSpecificWrapper(List candidates,
Class>[] matchingTypes, boolean elSpecific, String errorMsg) {
List ambiguouses = new ArrayList();
for (Wrapper candidate : candidates) {
boolean lessSpecific = false;
Iterator it = ambiguouses.iterator();
while(it.hasNext()) {
int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific);
if (result == 1) {
it.remove();
} else if (result == -1) {
lessSpecific = true;
}
}
if (!lessSpecific) {
ambiguouses.add(candidate);
}
}
if (ambiguouses.size() > 1) {
throw new MethodNotFoundException(errorMsg);
}
return ambiguouses.get(0);
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2,
Class>[] matchingTypes, boolean elSpecific) {
Class>[] paramTypes1 = wrapper1.getParameterTypes();
Class>[] paramTypes2 = wrapper2.getParameterTypes();
if (wrapper1.isVarArgs()) {
//JLS8 15.12.2.5 Choosing the Most Specific Method
int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length);
paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length);
paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length);
if (length > matchingTypes.length) {
Class>[] matchingTypes2 = new Class>[length];
System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length);
matchingTypes = matchingTypes2;
}
}
int result = 0;
for (int i = 0; i < paramTypes1.length; i++) {
if (paramTypes1[i] != paramTypes2[i]) {
int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific);
if (r2 == 1) {
if (result == -1) {
return 0;
}
result = 1;
} else if (r2 == -1) {
if (result == 1) {
return 0;
}
result = -1;
} else {
return 0;
}
}
}
if (result == 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.
result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge());
}
return result;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static int isMoreSpecific(Class> type1, Class> type2, Class> matchingType, boolean elSpecific) {
type1 = getBoxingTypeIfPrimitive(type1);
type2 = getBoxingTypeIfPrimitive(type2);
if (type2.isAssignableFrom(type1)) {
return 1;
} else if (type1.isAssignableFrom(type2)) {
return -1;
} else {
if (elSpecific) {
/*
* Number will be treated as more specific
*
* ASTInteger only return Long or BigInteger, no Byte / Short / Integer.
* ASTFloatingPoint also.
*
*/
if (matchingType != null && Number.class.isAssignableFrom(matchingType)) {
boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive();
boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive();
if (b1 && !b2) {
return 1;
} else if (b2 && !b1) {
return -1;
} else {
return 0;
}
}
return 0;
} else {
return 0;
}
}
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static Class> getBoxingTypeIfPrimitive(Class> clazz) {
if (clazz.isPrimitive()) {
if (clazz == Boolean.TYPE) {
return Boolean.class;
} else if (clazz == Character.TYPE) {
return Character.class;
} else if (clazz == Byte.TYPE) {
return Byte.class;
} else if (clazz == Short.TYPE) {
return Short.class;
} else if (clazz == Integer.TYPE) {
return Integer.class;
} else if (clazz == Long.TYPE) {
return Long.class;
} else if (clazz == Float.TYPE) {
return Float.class;
} else {
return Double.class;
}
} else {
return clazz;
}
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static Class>[] getComparingParamTypesForVarArgsMethod(Class>[] paramTypes, int length) {
Class>[] result = new Class>[length];
System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1);
Class> type = paramTypes[paramTypes.length - 1].getComponentType();
for (int i = paramTypes.length - 1; i < length; i++) {
result[i] = type;
}
return result;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static final String paramString(Class>[] types) {
if (types != null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < types.length; i++) {
if (types[i] == null) {
sb.append("null, ");
} else {
sb.append(types[i].getName()).append(", ");
}
}
if (sb.length() > 2) {
sb.setLength(sb.length() - 2);
}
return sb.toString();
}
return null;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
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;
}
target = getBoxingTypeIfPrimitive(target);
return target.isAssignableFrom(src);
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static boolean isCoercibleFrom(Object src, Class> target) {
// TODO: This isn't pretty but it works. Significant refactoring would
// be required to avoid the exception.
try {
ELSupport.coerceToType(src, target);
} catch (Exception e) {
return false;
}
return true;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static Class>[] getTypesFromValues(Object[] values) {
if (values == null) {
return null;
}
Class> result[] = new Class>[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
result[i] = null;
} else {
result[i] = values[i].getClass();
}
}
return result;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*
* Get a public method form a public class or interface of a given method.
* Note that if a PropertyDescriptor is obtained for a non-public class that
* implements a public interface, the read/write methods will be for the
* class, and therefore inaccessible. To correct this, a version of the
* same method must be found in a superclass or interface.
*
*/
static Method getMethod(Class> type, Method m) {
if (m == null || Modifier.isPublic(type.getModifiers())) {
return m;
}
Class>[] inf = type.getInterfaces();
Method mp = null;
for (int i = 0; i < inf.length; i++) {
try {
mp = inf[i].getMethod(m.getName(), m.getParameterTypes());
mp = getMethod(mp.getDeclaringClass(), 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(), mp);
if (mp != null) {
return mp;
}
} catch (NoSuchMethodException e) {
// Ignore
}
}
return null;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
static Constructor> getConstructor(Class> type, Constructor> c) {
if (c == null || Modifier.isPublic(type.getModifiers())) {
return c;
}
Constructor> cp = null;
Class> sup = type.getSuperclass();
if (sup != null) {
try {
cp = sup.getConstructor(c.getParameterTypes());
cp = getConstructor(cp.getDeclaringClass(), cp);
if (cp != null) {
return cp;
}
} catch (NoSuchMethodException e) {
// Ignore
}
}
return null;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
static Object[] buildParameters(ELContext context, Class>[] parameterTypes,
boolean isVarArgs,Object[] params) {
Object[] parameters = null;
if (parameterTypes.length > 0) {
parameters = new Object[parameterTypes.length];
int paramCount = params == null ? 0 : params.length;
if (isVarArgs) {
int varArgIndex = parameterTypes.length - 1;
// First argCount-1 parameters are standard
for (int i = 0; (i < varArgIndex && i < paramCount); i++) {
parameters[i] = context.convertToType(params[i],
parameterTypes[i]);
}
// Last parameter is the varargs
if (parameterTypes.length == paramCount
&& parameterTypes[varArgIndex] == params[varArgIndex].getClass()) {
parameters[varArgIndex] = params[varArgIndex];
} else {
Class> varArgClass =
parameterTypes[varArgIndex].getComponentType();
final Object varargs = Array.newInstance(
varArgClass,
(paramCount - varArgIndex));
for (int i = (varArgIndex); i < paramCount; i++) {
Array.set(varargs, i - varArgIndex,
context.convertToType(params[i], varArgClass));
}
parameters[varArgIndex] = varargs;
}
} else {
for (int i = 0; i < parameterTypes.length && i < paramCount; i++) {
parameters[i] = context.convertToType(params[i],
parameterTypes[i]);
}
}
}
return parameters;
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private abstract static class Wrapper {
public static List wrap(Method[] methods, String name) {
List result = new ArrayList<>();
for (Method method : methods) {
if (method.getName().equals(name)) {
result.add(new MethodWrapper(method));
}
}
return result;
}
public static List wrap(Constructor>[] constructors) {
List result = new ArrayList<>();
for (Constructor> constructor : constructors) {
result.add(new ConstructorWrapper(constructor));
}
return result;
}
public abstract Object unWrap();
public abstract Class>[] getParameterTypes();
public abstract boolean isVarArgs();
public abstract boolean isBridge();
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static class MethodWrapper extends Wrapper {
private final Method m;
public MethodWrapper(Method m) {
this.m = m;
}
@Override
public Object unWrap() {
return m;
}
@Override
public Class>[] getParameterTypes() {
return m.getParameterTypes();
}
@Override
public boolean isVarArgs() {
return m.isVarArgs();
}
@Override
public boolean isBridge() {
return m.isBridge();
}
}
/*
* This method duplicates code in javax.el.ELUtil. When
* making changes keep the code in sync.
*/
private static class ConstructorWrapper extends Wrapper {
private final Constructor> c;
public ConstructorWrapper(Constructor> c) {
this.c = c;
}
@Override
public Object unWrap() {
return c;
}
@Override
public Class>[] getParameterTypes() {
return c.getParameterTypes();
}
@Override
public boolean isVarArgs() {
return c.isVarArgs();
}
@Override
public boolean isBridge() {
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy