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