diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java')
-rw-r--r-- | propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java new file mode 100644 index 0000000..b362211 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.reflect; + +import com.google.common.collect.Maps; + +import org.eclipse.wb.internal.core.utils.check.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Map; + +/** + * Contains different Java reflection utilities. + * + * @author scheglov_ke + * @coverage core.util + */ +public class ReflectionUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private ReflectionUtils() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Signature + // + //////////////////////////////////////////////////////////////////////////// + /** + * @param runtime + * is <code>true</code> if we need name for class loading, <code>false</code> if we need + * name for source generation. + * + * @return the fully qualified name of given {@link Type}. + */ + public static String getFullyQualifiedName(Type type, boolean runtime) { + Assert.isNotNull(type); + // Class + if (type instanceof Class<?>) { + Class<?> clazz = (Class<?>) type; + // array + if (clazz.isArray()) { + return getFullyQualifiedName(clazz.getComponentType(), runtime) + "[]"; + } + // object + String name = clazz.getName(); + if (!runtime) { + name = name.replace('$', '.'); + } + return name; + } + // GenericArrayType + if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + return getFullyQualifiedName(genericArrayType.getGenericComponentType(), runtime) + "[]"; + } + // ParameterizedType + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type rawType = parameterizedType.getRawType(); + // raw type + StringBuilder sb = new StringBuilder(); + sb.append(getFullyQualifiedName(rawType, runtime)); + // type arguments + sb.append("<"); + boolean firstTypeArgument = true; + for (Type typeArgument : parameterizedType.getActualTypeArguments()) { + if (!firstTypeArgument) { + sb.append(","); + } + firstTypeArgument = false; + sb.append(getFullyQualifiedName(typeArgument, runtime)); + } + sb.append(">"); + // done + return sb.toString(); + } + // WildcardType + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + return "? extends " + getFullyQualifiedName(wildcardType.getUpperBounds()[0], runtime); + } + // TypeVariable + TypeVariable<?> typeVariable = (TypeVariable<?>) type; + return typeVariable.getName(); + } + + /** + * Appends fully qualified names of given parameter types (appends also <code>"()"</code>). + */ + private static void appendParameterTypes(StringBuilder buffer, Type[] parameterTypes) { + buffer.append('('); + boolean firstParameter = true; + for (Type parameterType : parameterTypes) { + if (firstParameter) { + firstParameter = false; + } else { + buffer.append(','); + } + buffer.append(getFullyQualifiedName(parameterType, false)); + } + buffer.append(')'); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Method + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return all declared {@link Method}'s, including protected and private. + */ + public static Map<String, Method> getMethods(Class<?> clazz) { + Map<String, Method> methods = Maps.newHashMap(); + // process classes + for (Class<?> c = clazz; c != null; c = c.getSuperclass()) { + for (Method method : c.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // process interfaces + for (Class<?> interfaceClass : clazz.getInterfaces()) { + for (Method method : interfaceClass.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // done + return methods; + } + + /** + * @return signature for given {@link Method}. This signature is not same signature as in JVM or + * JDT, just some string that unique identifies method in its {@link Class}. + */ + public static String getMethodSignature(Method method) { + Assert.isNotNull(method); + return getMethodSignature(method.getName(), method.getParameterTypes()); + } + + /** + * Returns the signature of {@link Method} with given combination of name and parameter types. + * This signature is not same signature as in JVM or JDT, just some string that unique identifies + * method in its {@link Class}. + * + * @param name + * the name of {@link Method}. + * @param parameterTypes + * the types of {@link Method} parameters. + * + * @return signature of {@link Method}. + */ + public static String getMethodSignature(String name, Type... parameterTypes) { + Assert.isNotNull(name); + Assert.isNotNull(parameterTypes); + // + StringBuilder buffer = new StringBuilder(); + buffer.append(name); + appendParameterTypes(buffer, parameterTypes); + return buffer.toString(); + } + + private static final ClassMap<Map<String, Method>> m_getMethodBySignature = ClassMap.create(); + + /** + * Returns the {@link Method} defined in {@link Class}. This method can have any visibility, i.e. + * we can find even protected/private methods. Can return <code>null</code> if no method with + * given signature found. + * + * @param clazz + * the {@link Class} to get method from it, or its superclass. + * @param signature + * the signature of method in same format as {@link #getMethodSignature(Method)}. + * + * @return the {@link Method} for given signature, or <code>null</code> if no such method found. + */ + public static Method getMethodBySignature(Class<?> clazz, String signature) { + Assert.isNotNull(clazz); + Assert.isNotNull(signature); + // prepare cache + Map<String, Method> cache = m_getMethodBySignature.get(clazz); + if (cache == null) { + cache = getMethods(clazz); + m_getMethodBySignature.put(clazz, cache); + } + // use cache + return cache.get(signature); + } + + /** + * @return the {@link Object} result of invoking method with given signature. + */ + public static Object invokeMethod(Object object, String signature, Object... arguments) + throws Exception { + Assert.isNotNull(object); + Assert.isNotNull(arguments); + // prepare class/object + Class<?> refClass = getRefClass(object); + Object refObject = getRefObject(object); + // prepare method + Method method = getMethodBySignature(refClass, signature); + Assert.isNotNull(method, "Can not find method " + signature + " in " + refClass); + // do invoke + try { + return method.invoke(refObject, arguments); + } catch (InvocationTargetException e) { + throw propagate(e.getCause()); + } + } + + /** + * Invokes method by name and parameter types. + * + * @param object + * the object to call, may be {@link Class} for invoking static method. + * @param name + * the name of method. + * @param parameterTypes + * the types of parameters. + * @param arguments + * the values of argument for invocation. + * + * @return the {@link Object} result of invoking method. + */ + public static Object invokeMethod2(Object object, + String name, + Class<?>[] parameterTypes, + Object[] arguments) throws Exception { + Assert.equals(parameterTypes.length, arguments.length); + String signature = getMethodSignature(name, parameterTypes); + return invokeMethod(object, signature, arguments); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Class} of given {@link Object} or casted object, if it is {@link Class} + * itself. + */ + private static Class<?> getRefClass(Object object) { + return object instanceof Class<?> ? (Class<?>) object : object.getClass(); + } + + /** + * @return the {@link Object} that should be used as argument for {@link Field#get(Object)} and + * {@link Method#invoke(Object, Object[])}. + */ + private static Object getRefObject(Object object) { + return object instanceof Class<?> ? null : object; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Throwable propagation + // + //////////////////////////////////////////////////////////////////////////// + /** + * Helper class used in {@link #propagate(Throwable)}. + */ + private static class ExceptionThrower { + private static Throwable throwable; + + private ExceptionThrower() throws Throwable { + if (System.getProperty("wbp.ReflectionUtils.propagate().InstantiationException") != null) { + throw new InstantiationException(); + } + if (System.getProperty("wbp.ReflectionUtils.propagate().IllegalAccessException") != null) { + throw new IllegalAccessException(); + } + throw throwable; + } + + public static synchronized void spit(Throwable t) { + if (System.getProperty("wbp.ReflectionUtils.propagate().dontThrow") == null) { + ExceptionThrower.throwable = t; + try { + ExceptionThrower.class.newInstance(); + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + } finally { + ExceptionThrower.throwable = null; + } + } + } + } + + /** + * Propagates {@code throwable} as-is without any wrapping. This is trick. + * + * @return nothing will ever be returned; this return type is only for your convenience, to use + * this method in "throw" statement. + */ + public static RuntimeException propagate(Throwable throwable) { + if (System.getProperty("wbp.ReflectionUtils.propagate().forceReturn") == null) { + ExceptionThrower.spit(throwable); + } + return null; + } +} |