View Javadoc
1   /*
2    * Copyright 2017 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import java.lang.reflect.AccessibleObject;
19  import java.lang.reflect.Array;
20  import java.lang.reflect.GenericArrayType;
21  import java.lang.reflect.ParameterizedType;
22  import java.lang.reflect.Type;
23  import java.lang.reflect.TypeVariable;
24  
25  public final class ReflectionUtil {
26  
27      private ReflectionUtil() { }
28  
29      /**
30       * Try to call {@link AccessibleObject#setAccessible(boolean)} but will catch any {@link SecurityException} and
31       * {@link java.lang.reflect.InaccessibleObjectException} and return it.
32       * The caller must check if it returns {@code null} and if not handle the returned exception.
33       */
34      public static Throwable trySetAccessible(AccessibleObject object, boolean checkAccessible) {
35          if (checkAccessible && !PlatformDependent0.isExplicitTryReflectionSetAccessible()) {
36              return new UnsupportedOperationException("Reflective setAccessible(true) disabled");
37          }
38          try {
39              object.setAccessible(true);
40              return null;
41          } catch (SecurityException e) {
42              return e;
43          } catch (RuntimeException e) {
44              return handleInaccessibleObjectException(e);
45          }
46      }
47  
48      private static RuntimeException handleInaccessibleObjectException(RuntimeException e) {
49          // JDK 9 can throw an inaccessible object exception here; since Netty compiles
50          // against JDK 7 and this exception was only added in JDK 9, we have to weakly
51          // check the type
52          if ("java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) {
53              return e;
54          }
55          throw e;
56      }
57  
58      private static Class<?> fail(Class<?> type, String typeParamName) {
59          throw new IllegalStateException(
60                  "cannot determine the type of the type parameter '" + typeParamName + "': " + type);
61      }
62  
63      /**
64       * Resolve a type parameter of a class that is a subclass of the given parametrized superclass.
65       * @param object The object to resolve the type parameter for
66       * @param parametrizedSuperclass The parametrized superclass
67       * @param typeParamName The name of the type parameter to resolve
68       * @return The resolved type parameter
69       * @throws IllegalStateException if the type parameter could not be resolved
70       * */
71      public static Class<?> resolveTypeParameter(final Object object,
72                                                  Class<?> parametrizedSuperclass,
73                                                  String typeParamName) {
74          final Class<?> thisClass = object.getClass();
75          Class<?> currentClass = thisClass;
76          for (;;) {
77              if (currentClass.getSuperclass() == parametrizedSuperclass) {
78                  int typeParamIndex = -1;
79                  TypeVariable<?>[] typeParams = currentClass.getSuperclass().getTypeParameters();
80                  for (int i = 0; i < typeParams.length; i ++) {
81                      if (typeParamName.equals(typeParams[i].getName())) {
82                          typeParamIndex = i;
83                          break;
84                      }
85                  }
86  
87                  if (typeParamIndex < 0) {
88                      throw new IllegalStateException(
89                              "unknown type parameter '" + typeParamName + "': " + parametrizedSuperclass);
90                  }
91  
92                  Type genericSuperType = currentClass.getGenericSuperclass();
93                  if (!(genericSuperType instanceof ParameterizedType)) {
94                      return Object.class;
95                  }
96  
97                  Type[] actualTypeParams = ((ParameterizedType) genericSuperType).getActualTypeArguments();
98  
99                  Type actualTypeParam = actualTypeParams[typeParamIndex];
100                 if (actualTypeParam instanceof ParameterizedType) {
101                     actualTypeParam = ((ParameterizedType) actualTypeParam).getRawType();
102                 }
103                 if (actualTypeParam instanceof Class) {
104                     return (Class<?>) actualTypeParam;
105                 }
106                 if (actualTypeParam instanceof GenericArrayType) {
107                     Type componentType = ((GenericArrayType) actualTypeParam).getGenericComponentType();
108                     if (componentType instanceof ParameterizedType) {
109                         componentType = ((ParameterizedType) componentType).getRawType();
110                     }
111                     if (componentType instanceof Class) {
112                         return Array.newInstance((Class<?>) componentType, 0).getClass();
113                     }
114                 }
115                 if (actualTypeParam instanceof TypeVariable) {
116                     // Resolved type parameter points to another type parameter.
117                     TypeVariable<?> v = (TypeVariable<?>) actualTypeParam;
118                     if (!(v.getGenericDeclaration() instanceof Class)) {
119                         return Object.class;
120                     }
121 
122                     currentClass = thisClass;
123                     parametrizedSuperclass = (Class<?>) v.getGenericDeclaration();
124                     typeParamName = v.getName();
125                     if (parametrizedSuperclass.isAssignableFrom(thisClass)) {
126                         continue;
127                     }
128                     return Object.class;
129                 }
130 
131                 return fail(thisClass, typeParamName);
132             }
133             currentClass = currentClass.getSuperclass();
134             if (currentClass == null) {
135                 return fail(thisClass, typeParamName);
136             }
137         }
138     }
139 }