summaryrefslogtreecommitdiff
path: root/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java
diff options
context:
space:
mode:
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.java327
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;
+ }
+}