View Javadoc
1   /*
2    * Copyright 2013 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    *   http://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  
17  package io.netty.util.internal;
18  
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  import java.util.HashMap;
25  import java.util.Map;
26  
27  public abstract class TypeParameterMatcher {
28  
29      private static final TypeParameterMatcher NOOP = new NoOpTypeParameterMatcher();
30      private static final Object TEST_OBJECT = new Object();
31  
32      public static TypeParameterMatcher get(final Class<?> parameterType) {
33          final Map<Class<?>, TypeParameterMatcher> getCache =
34                  InternalThreadLocalMap.get().typeParameterMatcherGetCache();
35  
36          TypeParameterMatcher matcher = getCache.get(parameterType);
37          if (matcher == null) {
38              if (parameterType == Object.class) {
39                  matcher = NOOP;
40              } else if (PlatformDependent.hasJavassist()) {
41                  try {
42                      matcher = JavassistTypeParameterMatcherGenerator.generate(parameterType);
43                      matcher.match(TEST_OBJECT);
44                  } catch (IllegalAccessError e) {
45                      // Happens if parameterType is not public.
46                      matcher = null;
47                  } catch (Exception e) {
48                      // Will not usually happen, but just in case.
49                      matcher = null;
50                  }
51              }
52  
53              if (matcher == null) {
54                  matcher = new ReflectiveMatcher(parameterType);
55              }
56  
57              getCache.put(parameterType, matcher);
58          }
59  
60          return matcher;
61      }
62  
63      public static TypeParameterMatcher find(
64              final Object object, final Class<?> parameterizedSuperclass, final String typeParamName) {
65  
66          final Map<Class<?>, Map<String, TypeParameterMatcher>> findCache =
67                  InternalThreadLocalMap.get().typeParameterMatcherFindCache();
68          final Class<?> thisClass = object.getClass();
69  
70          Map<String, TypeParameterMatcher> map = findCache.get(thisClass);
71          if (map == null) {
72              map = new HashMap<String, TypeParameterMatcher>();
73              findCache.put(thisClass, map);
74          }
75  
76          TypeParameterMatcher matcher = map.get(typeParamName);
77          if (matcher == null) {
78              matcher = get(find0(object, parameterizedSuperclass, typeParamName));
79              map.put(typeParamName, matcher);
80          }
81  
82          return matcher;
83      }
84  
85      private static Class<?> find0(
86              final Object object, Class<?> parameterizedSuperclass, String typeParamName) {
87  
88          final Class<?> thisClass = object.getClass();
89          Class<?> currentClass = thisClass;
90          for (;;) {
91              if (currentClass.getSuperclass() == parameterizedSuperclass) {
92                  int typeParamIndex = -1;
93                  TypeVariable<?>[] typeParams = currentClass.getSuperclass().getTypeParameters();
94                  for (int i = 0; i < typeParams.length; i ++) {
95                      if (typeParamName.equals(typeParams[i].getName())) {
96                          typeParamIndex = i;
97                          break;
98                      }
99                  }
100 
101                 if (typeParamIndex < 0) {
102                     throw new IllegalStateException(
103                             "unknown type parameter '" + typeParamName + "': " + parameterizedSuperclass);
104                 }
105 
106                 Type genericSuperType = currentClass.getGenericSuperclass();
107                 if (!(genericSuperType instanceof ParameterizedType)) {
108                     return Object.class;
109                 }
110 
111                 Type[] actualTypeParams = ((ParameterizedType) genericSuperType).getActualTypeArguments();
112 
113                 Type actualTypeParam = actualTypeParams[typeParamIndex];
114                 if (actualTypeParam instanceof ParameterizedType) {
115                     actualTypeParam = ((ParameterizedType) actualTypeParam).getRawType();
116                 }
117                 if (actualTypeParam instanceof Class) {
118                     return (Class<?>) actualTypeParam;
119                 }
120                 if (actualTypeParam instanceof GenericArrayType) {
121                     Type componentType = ((GenericArrayType) actualTypeParam).getGenericComponentType();
122                     if (componentType instanceof ParameterizedType) {
123                         componentType = ((ParameterizedType) componentType).getRawType();
124                     }
125                     if (componentType instanceof Class) {
126                         return Array.newInstance((Class<?>) componentType, 0).getClass();
127                     }
128                 }
129                 if (actualTypeParam instanceof TypeVariable) {
130                     // Resolved type parameter points to another type parameter.
131                     TypeVariable<?> v = (TypeVariable<?>) actualTypeParam;
132                     currentClass = thisClass;
133                     if (!(v.getGenericDeclaration() instanceof Class)) {
134                         return Object.class;
135                     }
136 
137                     parameterizedSuperclass = (Class<?>) v.getGenericDeclaration();
138                     typeParamName = v.getName();
139                     if (parameterizedSuperclass.isAssignableFrom(thisClass)) {
140                         continue;
141                     } else {
142                         return Object.class;
143                     }
144                 }
145 
146                 return fail(thisClass, typeParamName);
147             }
148             currentClass = currentClass.getSuperclass();
149             if (currentClass == null) {
150                 return fail(thisClass, typeParamName);
151             }
152         }
153     }
154 
155     private static Class<?> fail(Class<?> type, String typeParamName) {
156         throw new IllegalStateException(
157                 "cannot determine the type of the type parameter '" + typeParamName + "': " + type);
158     }
159 
160     public abstract boolean match(Object msg);
161 
162     private static final class ReflectiveMatcher extends TypeParameterMatcher {
163         private final Class<?> type;
164 
165         ReflectiveMatcher(Class<?> type) {
166             this.type = type;
167         }
168 
169         @Override
170         public boolean match(Object msg) {
171             return type.isInstance(msg);
172         }
173     }
174 
175     protected TypeParameterMatcher() { }
176 }