1765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye/*******************************************************************************
2765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Copyright (c) 2011 Google, Inc.
3765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * All rights reserved. This program and the accompanying materials
4765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * are made available under the terms of the Eclipse Public License v1.0
5765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * which accompanies this distribution, and is available at
6765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * http://www.eclipse.org/legal/epl-v10.html
7765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *
8765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Contributors:
9765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *    Google, Inc. - initial API and implementation
10765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *******************************************************************************/
11765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyepackage org.eclipse.wb.internal.core.utils.reflect;
12765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
13765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport com.google.common.collect.Maps;
14765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
15765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.utils.check.Assert;
16765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
17765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.Field;
18765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.GenericArrayType;
19765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.InvocationTargetException;
20765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.Method;
21765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.ParameterizedType;
22765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.Type;
23765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.TypeVariable;
24765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.lang.reflect.WildcardType;
25765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.util.Map;
26765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
27765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye/**
28765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Contains different Java reflection utilities.
29765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *
30765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * @author scheglov_ke
31765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * @coverage core.util
32765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye */
33765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyepublic class ReflectionUtils {
34765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
35765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
36765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Constructor
37765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
38765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
39765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private ReflectionUtils() {
40765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
41765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
42765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
43765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
44765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Signature
45765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
46765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
47765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
48765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param runtime
49765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          is <code>true</code> if we need name for class loading, <code>false</code> if we need
50765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          name for source generation.
51765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
52765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the fully qualified name of given {@link Type}.
53765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
54765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static String getFullyQualifiedName(Type type, boolean runtime) {
55765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(type);
56765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // Class
57765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (type instanceof Class<?>) {
58765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Class<?> clazz = (Class<?>) type;
59765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // array
60765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (clazz.isArray()) {
61765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        return getFullyQualifiedName(clazz.getComponentType(), runtime) + "[]";
62765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
63765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // object
64765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String name = clazz.getName();
65765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (!runtime) {
66765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        name = name.replace('$', '.');
67765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
68765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return name;
69765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
70765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // GenericArrayType
71765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (type instanceof GenericArrayType) {
72765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      GenericArrayType genericArrayType = (GenericArrayType) type;
73765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return getFullyQualifiedName(genericArrayType.getGenericComponentType(), runtime) + "[]";
74765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
75765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // ParameterizedType
76765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (type instanceof ParameterizedType) {
77765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      ParameterizedType parameterizedType = (ParameterizedType) type;
78765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Type rawType = parameterizedType.getRawType();
79765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // raw type
80765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      StringBuilder sb = new StringBuilder();
81765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      sb.append(getFullyQualifiedName(rawType, runtime));
82765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // type arguments
83765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      sb.append("<");
84765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      boolean firstTypeArgument = true;
85765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
86765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (!firstTypeArgument) {
87765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          sb.append(",");
88765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
89765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        firstTypeArgument = false;
90765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        sb.append(getFullyQualifiedName(typeArgument, runtime));
91765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
92765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      sb.append(">");
93765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // done
94765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return sb.toString();
95765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
96765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // WildcardType
97765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (type instanceof WildcardType) {
98765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      WildcardType wildcardType = (WildcardType) type;
99765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return "? extends " + getFullyQualifiedName(wildcardType.getUpperBounds()[0], runtime);
100765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
101765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // TypeVariable
102765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    TypeVariable<?> typeVariable = (TypeVariable<?>) type;
103765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return typeVariable.getName();
104765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
105765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
106765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
107765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Appends fully qualified names of given parameter types (appends also <code>"()"</code>).
108765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
109765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static void appendParameterTypes(StringBuilder buffer, Type[] parameterTypes) {
110765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    buffer.append('(');
111765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    boolean firstParameter = true;
112765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    for (Type parameterType : parameterTypes) {
113765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (firstParameter) {
114765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        firstParameter = false;
115765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      } else {
116765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        buffer.append(',');
117765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
118765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      buffer.append(getFullyQualifiedName(parameterType, false));
119765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
120765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    buffer.append(')');
121765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
122765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
123765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
124765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
125765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Method
126765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
127765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
128765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
129765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return all declared {@link Method}'s, including protected and private.
130765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
131765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Map<String, Method> getMethods(Class<?> clazz) {
132765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Map<String, Method> methods = Maps.newHashMap();
133765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // process classes
134765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
135765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      for (Method method : c.getDeclaredMethods()) {
136765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        String signature = getMethodSignature(method);
137765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (!methods.containsKey(signature)) {
138765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          method.setAccessible(true);
139765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          methods.put(signature, method);
140765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
141765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
142765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
143765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // process interfaces
144765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    for (Class<?> interfaceClass : clazz.getInterfaces()) {
145765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      for (Method method : interfaceClass.getDeclaredMethods()) {
146765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        String signature = getMethodSignature(method);
147765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (!methods.containsKey(signature)) {
148765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          method.setAccessible(true);
149765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          methods.put(signature, method);
150765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
151765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
152765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
153765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // done
154765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return methods;
155765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
156765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
157765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
158765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return signature for given {@link Method}. This signature is not same signature as in JVM or
159765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *         JDT, just some string that unique identifies method in its {@link Class}.
160765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
161765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static String getMethodSignature(Method method) {
162765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(method);
163765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return getMethodSignature(method.getName(), method.getParameterTypes());
164765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
165765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
166765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
167765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Returns the signature of {@link Method} with given combination of name and parameter types.
168765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * This signature is not same signature as in JVM or JDT, just some string that unique identifies
169765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * method in its {@link Class}.
170765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
171765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param name
172765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the name of {@link Method}.
173765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param parameterTypes
174765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the types of {@link Method} parameters.
175765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
176765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return signature of {@link Method}.
177765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
178765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static String getMethodSignature(String name, Type... parameterTypes) {
179765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(name);
180765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(parameterTypes);
181765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    //
182765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    StringBuilder buffer = new StringBuilder();
183765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    buffer.append(name);
184765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    appendParameterTypes(buffer, parameterTypes);
185765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return buffer.toString();
186765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
187765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
188765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static final ClassMap<Map<String, Method>> m_getMethodBySignature = ClassMap.create();
189765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
190765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
191765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Returns the {@link Method} defined in {@link Class}. This method can have any visibility, i.e.
192765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * we can find even protected/private methods. Can return <code>null</code> if no method with
193765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * given signature found.
194765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
195765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param clazz
196765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the {@link Class} to get method from it, or its superclass.
197765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param signature
198765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the signature of method in same format as {@link #getMethodSignature(Method)}.
199765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
200765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the {@link Method} for given signature, or <code>null</code> if no such method found.
201765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
202765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Method getMethodBySignature(Class<?> clazz, String signature) {
203765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(clazz);
204765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(signature);
205765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare cache
206765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Map<String, Method> cache = m_getMethodBySignature.get(clazz);
207765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (cache == null) {
208765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      cache = getMethods(clazz);
209765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      m_getMethodBySignature.put(clazz, cache);
210765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
211765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // use cache
212765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return cache.get(signature);
213765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
214765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
215765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
216765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the {@link Object} result of invoking method with given signature.
217765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
218765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Object invokeMethod(Object object, String signature, Object... arguments)
219765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      throws Exception {
220765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(object);
221765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(arguments);
222765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare class/object
223765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Class<?> refClass = getRefClass(object);
224765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Object refObject = getRefObject(object);
225765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare method
226765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Method method = getMethodBySignature(refClass, signature);
227765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.isNotNull(method, "Can not find method " + signature + " in " + refClass);
228765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // do invoke
229765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    try {
230765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return method.invoke(refObject, arguments);
231765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    } catch (InvocationTargetException e) {
232765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      throw propagate(e.getCause());
233765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
234765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
235765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
236765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
237765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Invokes method by name and parameter types.
238765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
239765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param object
240765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the object to call, may be {@link Class} for invoking static method.
241765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param name
242765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the name of method.
243765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param parameterTypes
244765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the types of parameters.
245765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param arguments
246765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the values of argument for invocation.
247765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
248765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the {@link Object} result of invoking method.
249765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
250765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Object invokeMethod2(Object object,
251765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String name,
252765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Class<?>[] parameterTypes,
253765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Object[] arguments) throws Exception {
254765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Assert.equals(parameterTypes.length, arguments.length);
255765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    String signature = getMethodSignature(name, parameterTypes);
256765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return invokeMethod(object, signature, arguments);
257765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
258765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
259765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
260765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
261765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Utils
262765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
263765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
264765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
265765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the {@link Class} of given {@link Object} or casted object, if it is {@link Class}
266765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *         itself.
267765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
268765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static Class<?> getRefClass(Object object) {
269765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return object instanceof Class<?> ? (Class<?>) object : object.getClass();
270765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
271765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
272765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
273765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the {@link Object} that should be used as argument for {@link Field#get(Object)} and
274765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *         {@link Method#invoke(Object, Object[])}.
275765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
276765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static Object getRefObject(Object object) {
277765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return object instanceof Class<?> ? null : object;
278765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
279765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
280765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
281765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
282765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Throwable propagation
283765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  //
284765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  ////////////////////////////////////////////////////////////////////////////
285765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
286765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Helper class used in {@link #propagate(Throwable)}.
287765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
288765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static class ExceptionThrower {
289765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    private static Throwable throwable;
290765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
291765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    private ExceptionThrower() throws Throwable {
292765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (System.getProperty("wbp.ReflectionUtils.propagate().InstantiationException") != null) {
293765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        throw new InstantiationException();
294765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
295765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (System.getProperty("wbp.ReflectionUtils.propagate().IllegalAccessException") != null) {
296765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        throw new IllegalAccessException();
297765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
298765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      throw throwable;
299765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
300765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
301765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    public static synchronized void spit(Throwable t) {
302765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (System.getProperty("wbp.ReflectionUtils.propagate().dontThrow") == null) {
303765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        ExceptionThrower.throwable = t;
304765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        try {
305765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          ExceptionThrower.class.newInstance();
306765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        } catch (InstantiationException e) {
307765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        } catch (IllegalAccessException e) {
308765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        } finally {
309765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          ExceptionThrower.throwable = null;
310765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
311765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
312765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
313765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
314765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
315765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
316765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Propagates {@code throwable} as-is without any wrapping. This is trick.
317765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
318765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return nothing will ever be returned; this return type is only for your convenience, to use
319765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *         this method in "throw" statement.
320765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
321765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static RuntimeException propagate(Throwable throwable) {
322765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (System.getProperty("wbp.ReflectionUtils.propagate().forceReturn") == null) {
323765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      ExceptionThrower.spit(throwable);
324765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
325765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return null;
326765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
327765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye}
328