/* * Licensed 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.jboss.el.util; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.el.ELException; import javax.el.MethodInfo; import javax.el.MethodNotFoundException; import org.jboss.el.lang.ELSupport; /** * Utilities for Managing Serialization and Reflection * * @author Jacob Hookom [jacob@hookom.net] * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: markt $ */ @SuppressWarnings({"rawtypes", "unchecked", "unused"}) public final 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 * @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 */ 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; } private static Method pickBest(Class[] paramTypes, Method a, Method b) { int r = 0; for (int i = 0; i < paramTypes.length; i++) { if (paramTypes[i] != null) { r += matches(paramTypes[i], a.getParameterTypes()[i]); r -= matches(paramTypes[i], b.getParameterTypes()[i]); } } return (r >= 0) ? a : b; } private static int matches(Class t, Class p) { if (t == p || t.equals(p)) { return 2; } if (p.isAssignableFrom(t)) { return 1; } return 0; } private static ReferenceCache<Class, MethodCache> methodCache = new ReferenceCache<Class, MethodCache>(ReferenceCache.Type.Weak, ReferenceCache.Type.Soft) { public MethodCache create(Class key) { return new MethodCache(key); } }; private static final class MethodCache { private final Method[] methods; private final Class type; private final Map<String,Object> cache; public MethodCache(Class type) { boolean isAnonymous = type.isAnonymousClass(); boolean isPrivate = !Modifier.isPublic(type.getModifiers()); this.type = type; this.methods = type.getMethods(); this.cache = new HashMap<String,Object>(); Object c; for (Method m : this.methods) { if ((isPrivate || isAnonymous) && Modifier.isPublic(m.getModifiers())) { m.setAccessible(true); } c = this.cache.get(m.getName()); if (c == null) { this.cache.put(m.getName(), m); } else if (c instanceof Method) { List l = new ArrayList(5); l.add(m); l.add(c); this.cache.put(m.getName(), l); } else { ((List) c).add(m); } } } public Class getType() { return this.type; } public Method findMethod(String name, Object[] in) { Object o = this.cache.get(name); if (o == null) return null; if (o instanceof Method) return (Method) o; Method r = null; Class[] types = paramTypes(in); for (Method m : (List<Method>) o) { if (m.getParameterTypes().length == types.length) { if (r == null) { r = m; } else { r = pickBest(types, r, m); } } } return r; } } public static Method findMethod(Object base, Object name, Object[] params) { Method r = null; if (base != null && name != null) { Class type = base.getClass(); String methodName = ELSupport.coerceToString(name); MethodCache m = methodCache.get(type); // if (m == null || type != m.getType()) { // m = new MethodCache(type); // methodCache.set(type, m); // } r = m.findMethod(methodName, params); if (r == null) { throw new MethodNotFoundException(MessageFactory.get( "error.method.notfound", base, name, paramString(paramTypes(params)))); } } else { throw new MethodNotFoundException(); } return r; } /** * Returns a method based on the criteria * * @param base * the object that owns the method * @param property * the name of the method * @param paramTypes * the parameter types to use * @return the method specified * @throws MethodNotFoundException */ public static Method getMethod(Object base, Object property, Class[] paramTypes) 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(); Method method = null; try { method = base.getClass().getMethod(methodName, paramTypes); } catch (NoSuchMethodException nsme) { throw new MethodNotFoundException(MessageFactory.get( "error.method.notfound", base, property, paramString(paramTypes))); } return method; } public static MethodInfo getMethodInfo(Object base, Object property, Class[] paramTypes) throws MethodNotFoundException { Method m = ReflectionUtil.getMethod(base, property, paramTypes); return new MethodInfo(m.getName(), m.getReturnType(), m .getParameterTypes()); } public static MethodInfo getMethodInfo(Object base, Object property, Object[] paramValues) throws MethodNotFoundException { Method m = ReflectionUtil.findMethod(base, property, paramValues); return new MethodInfo(m.getName(), m.getReturnType(), m .getParameterTypes()); } public static Object invokeMethod(Object base, Object property, Object[] paramValues) throws ELException { Method m = ReflectionUtil.findMethod(base, property, paramValues); return invokeMethod(base, m, paramValues); } private static final Object[] EMPTY_PARAMS = new Object[0]; public static Object invokeMethod(Object base, Method m, Object[] paramValues) throws ELException { if (m == null) throw new MethodNotFoundException(); Class[] paramTypes = m.getParameterTypes(); Object[] params = null; if (paramTypes.length == 0) { // leave params null } else if (paramValues == null) { throw new MethodNotFoundException(m.getDeclaringClass() + "." + m.getName() + " has " + paramTypes.length + " params"); } else if (m.isVarArgs()) { // add values params = new Object[paramTypes.length]; int i = 0; for (; i < paramTypes.length - 1; i++) { params[i] = ELSupport.coerceToType(paramValues[i], paramTypes[i]); } Class argType = paramTypes[i].getComponentType(); if (paramTypes.length == paramValues.length) { if (paramValues[i] == null) { params[i] = Array.newInstance(argType, 0); } else if (paramValues[i].getClass().isArray()) { params[i] = paramValues[i]; } else { params[i] = Array.newInstance(argType, 1); Array.set(params[i], 0, ELSupport.coerceToType(paramValues[i], argType)); } } else { int len = paramValues.length - paramTypes.length + 1; Object ar = Array.newInstance(argType, len); for (int j = 0; j < len; j++) { Array.set(ar, j, ELSupport.coerceToType(paramValues[paramTypes.length - 1 + j], argType)); } params[i] = ar; } } else if (paramValues.length == paramTypes.length) { // add values params = new Object[paramTypes.length]; // assign first set for (int i = 0; i < paramTypes.length; i++) { params[i] = ELSupport.coerceToType(paramValues[i], paramTypes[i]); } } else { throw new MethodNotFoundException(m.getDeclaringClass().getName() + "." + m.getName() + " has " + paramTypes.length + ", only passed " + paramValues.length + " parameters"); } try { return m.invoke(base, params); } catch (IllegalAccessException iae) { throw new ELException(iae); } catch (InvocationTargetException ite) { throw new ELException(ite.getCause()); } } public static Object invokeMethod(Object base, Object property, Class[] paramTypes, Object[] paramValues) throws ELException, MethodNotFoundException { Method m = getMethod(base, property, paramTypes); return invokeMethod(base, m, paramValues); } protected static final String paramString(Class[] types) { if (types != null) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < types.length; i++) { if (types[i] != null) { sb.append(types[i].getName()).append(", "); } else { sb.append("null, "); } } if (sb.length() > 2) { sb.setLength(sb.length() - 2); } return sb.toString(); } return null; } private static Class[] NO_TYPES = new Class[0]; protected static final Class[] paramTypes(Object[] ar) { if (ar != null) { Class[] p = new Class[ar.length]; for (int i = 0; i < ar.length; i++) { if (ar[i] != null) { p[i] = ar[i].getClass(); } } return p; } return NO_TYPES; } }