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