KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > core > GenericCollectionTypeResolver


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.core;
18
19 import java.lang.reflect.Array JavaDoc;
20 import java.lang.reflect.GenericArrayType JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.ParameterizedType JavaDoc;
23 import java.lang.reflect.Type JavaDoc;
24 import java.lang.reflect.WildcardType JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import org.springframework.util.Assert;
29
30 /**
31  * Helper class for determining element types of collections and maps.
32  *
33  * <p>Mainly intended for usage within the framework, determining the
34  * target type of values to be added to a collection or map
35  * (to be able to attempt type conversion if appropriate).
36  *
37  * <p>Only usable on Java 5. Use an appropriate JdkVersion check before
38  * calling this class, if a fallback for JDK 1.3/1.4 is desirable.
39  *
40  * @author Juergen Hoeller
41  * @since 2.0
42  * @see org.springframework.beans.BeanWrapperImpl
43  * @see JdkVersion
44  */

45 public abstract class GenericCollectionTypeResolver {
46
47     /**
48      * Determine the generic element type of the given Collection class
49      * (if it declares one through a generic superclass or generic interface).
50      * @param collectionClass the collection class to introspect
51      * @return the generic type, or <code>null</code> if none
52      */

53     public static Class JavaDoc getCollectionType(Class JavaDoc collectionClass) {
54         return extractTypeFromClass(collectionClass, Collection JavaDoc.class, 0);
55     }
56
57     /**
58      * Determine the generic key type of the given Map class
59      * (if it declares one through a generic superclass or generic interface).
60      * @param mapClass the map class to introspect
61      * @return the generic type, or <code>null</code> if none
62      */

63     public static Class JavaDoc getMapKeyType(Class JavaDoc mapClass) {
64         return extractTypeFromClass(mapClass, Map JavaDoc.class, 0);
65     }
66
67     /**
68      * Determine the generic value type of the given Map class
69      * (if it declares one through a generic superclass or generic interface).
70      * @param mapClass the map class to introspect
71      * @return the generic type, or <code>null</code> if none
72      */

73     public static Class JavaDoc getMapValueType(Class JavaDoc mapClass) {
74         return extractTypeFromClass(mapClass, Map JavaDoc.class, 1);
75     }
76
77     /**
78      * Determine the generic element type of the given Collection parameter.
79      * @param methodParam the method parameter specification
80      * @return the generic type, or <code>null</code> if none
81      */

82     public static Class JavaDoc getCollectionParameterType(MethodParameter methodParam) {
83         return getGenericParameterType(methodParam, Collection JavaDoc.class, 0);
84     }
85
86     /**
87      * Determine the generic key type of the given Map parameter.
88      * @param methodParam the method parameter specification
89      * @return the generic type, or <code>null</code> if none
90      */

91     public static Class JavaDoc getMapKeyParameterType(MethodParameter methodParam) {
92         return getGenericParameterType(methodParam, Map JavaDoc.class, 0);
93     }
94
95     /**
96      * Determine the generic value type of the given Map parameter.
97      * @param methodParam the method parameter specification
98      * @return the generic type, or <code>null</code> if none
99      */

100     public static Class JavaDoc getMapValueParameterType(MethodParameter methodParam) {
101         return getGenericParameterType(methodParam, Map JavaDoc.class, 1);
102     }
103
104     /**
105      * Determine the generic element type of the given Collection return type.
106      * @param method the method to check the return type for
107      * @return the generic type, or <code>null</code> if none
108      */

109     public static Class JavaDoc getCollectionReturnType(Method JavaDoc method) {
110         return getGenericReturnType(method, Collection JavaDoc.class, 0, 1);
111     }
112
113     /**
114      * Determine the generic element type of the given Collection return type.
115      * <p>If the specified nesting level is higher than 1, the element type of
116      * a nested Collection/Map will be analyzed.
117      * @param method the method to check the return type for
118      * @param nestingLevel the nesting level of the target type
119      * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
120      * nested List, whereas 2 would indicate the element of the nested List)
121      * @return the generic type, or <code>null</code> if none
122      */

123     public static Class JavaDoc getCollectionReturnType(Method JavaDoc method, int nestingLevel) {
124         return getGenericReturnType(method, Collection JavaDoc.class, 0, nestingLevel);
125     }
126
127     /**
128      * Determine the generic key type of the given Map parameter.
129      * @param method the method to check the return type for
130      * @return the generic type, or <code>null</code> if none
131      */

132     public static Class JavaDoc getMapKeyReturnType(Method JavaDoc method) {
133         return getGenericReturnType(method, Map JavaDoc.class, 0, 1);
134     }
135
136     /**
137      * Determine the generic key type of the given Map parameter.
138      * @param method the method to check the return type for
139      * @param nestingLevel the nesting level of the target type
140      * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
141      * nested List, whereas 2 would indicate the element of the nested List)
142      * @return the generic type, or <code>null</code> if none
143      */

144     public static Class JavaDoc getMapKeyReturnType(Method JavaDoc method, int nestingLevel) {
145         return getGenericReturnType(method, Map JavaDoc.class, 0, nestingLevel);
146     }
147
148     /**
149      * Determine the generic value type of the given Map parameter.
150      * @param method the method to check the return type for
151      * @return the generic type, or <code>null</code> if none
152      */

153     public static Class JavaDoc getMapValueReturnType(Method JavaDoc method) {
154         return getGenericReturnType(method, Map JavaDoc.class, 1, 1);
155     }
156
157     /**
158      * Determine the generic value type of the given Map parameter.
159      * @param method the method to check the return type for
160      * @param nestingLevel the nesting level of the target type
161      * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
162      * nested List, whereas 2 would indicate the element of the nested List)
163      * @return the generic type, or <code>null</code> if none
164      */

165     public static Class JavaDoc getMapValueReturnType(Method JavaDoc method, int nestingLevel) {
166         return getGenericReturnType(method, Map JavaDoc.class, 1, nestingLevel);
167     }
168
169
170     /**
171      * Extract the generic parameter type from the given method or constructor.
172      * @param methodParam the method parameter specification
173      * @param source the source class/interface defining the generic parameter types
174      * @param typeIndex the index of the type (e.g. 0 for Collections,
175      * 0 for Map keys, 1 for Map values)
176      * @return the generic type, or <code>null</code> if none
177      */

178     private static Class JavaDoc getGenericParameterType(MethodParameter methodParam, Class JavaDoc source, int typeIndex) {
179         Assert.notNull(methodParam, "MethodParameter must not be null");
180         return extractType(methodParam, getTargetType(methodParam), source, typeIndex, methodParam.getNestingLevel());
181     }
182
183     /**
184      * Determine the target type for the given parameter specification.
185      * @param methodParam the method parameter specification
186      * @return the corresponding generic parameter or return type
187      */

188     private static Type JavaDoc getTargetType(MethodParameter methodParam) {
189         if (methodParam.getConstructor() != null) {
190             return methodParam.getConstructor().getGenericParameterTypes()[methodParam.getParameterIndex()];
191         }
192         else {
193             if (methodParam.getParameterIndex() >= 0) {
194                 return methodParam.getMethod().getGenericParameterTypes()[methodParam.getParameterIndex()];
195             }
196             else {
197                 return methodParam.getMethod().getGenericReturnType();
198             }
199         }
200     }
201
202     /**
203      * Extract the generic return type from the given method.
204      * @param method the method to check the return type for
205      * @param source the source class/interface defining the generic parameter types
206      * @param typeIndex the index of the type (e.g. 0 for Collections,
207      * 0 for Map keys, 1 for Map values)
208      * @param nestingLevel the nesting level of the target type
209      * @return the generic type, or <code>null</code> if none
210      */

211     private static Class JavaDoc getGenericReturnType(Method JavaDoc method, Class JavaDoc source, int typeIndex, int nestingLevel) {
212         Assert.notNull(method, "Method must not be null");
213         return extractType(null, method.getGenericReturnType(), source, typeIndex, nestingLevel);
214     }
215
216     /**
217      * Extract the generic type from the given Type object.
218      * @param methodParam the method parameter specification
219      * @param type the Type to check
220      * @param source the source collection/map Class that we check
221      * @param typeIndex the index of the actual type argument
222      * @param nestingLevel the nesting level of the target type
223      * @return the generic type as Class, or <code>null</code> if none
224      */

225     private static Class JavaDoc extractType(
226             MethodParameter methodParam, Type JavaDoc type, Class JavaDoc source, int typeIndex, int nestingLevel) {
227
228         Assert.isTrue(typeIndex >= 0, "Type index must be >=1");
229         Assert.isTrue(nestingLevel >= 0, "Nesting level must be >=1");
230         if (type instanceof ParameterizedType JavaDoc) {
231             return extractTypeFromParameterizedType(
232                     methodParam, (ParameterizedType JavaDoc) type, source, typeIndex, nestingLevel);
233         }
234         if (type instanceof Class JavaDoc) {
235             return extractTypeFromClass(methodParam, (Class JavaDoc) type, source, typeIndex, nestingLevel);
236         }
237         return null;
238     }
239
240     /**
241      * Extract the generic type from the given ParameterizedType object.
242      * @param methodParam the method parameter specification
243      * @param ptype the ParameterizedType to check
244      * @param source the expected raw source type (can be <code>null</code>)
245      * @param typeIndex the index of the actual type argument
246      * @param nestingLevel the nesting level of the target type
247      * @return the generic type as Class, or <code>null</code> if none
248      */

249     private static Class JavaDoc extractTypeFromParameterizedType(
250             MethodParameter methodParam, ParameterizedType JavaDoc ptype, Class JavaDoc source, int typeIndex, int nestingLevel) {
251
252         if (!(ptype.getRawType() instanceof Class JavaDoc)) {
253             return null;
254         }
255         Class JavaDoc rawType = (Class JavaDoc) ptype.getRawType();
256         Type JavaDoc[] paramTypes = ptype.getActualTypeArguments();
257         if (nestingLevel > 1) {
258             Integer JavaDoc currentTypeIndex =
259                     (methodParam != null ? methodParam.getTypeIndexForLevel(nestingLevel) : null);
260             // Default is last parameter type: Collection element or Map value.
261
int indexToUse = (currentTypeIndex != null ? currentTypeIndex.intValue() : paramTypes.length - 1);
262             Type JavaDoc paramType = paramTypes[indexToUse];
263             return extractType(methodParam, paramType, source, typeIndex, nestingLevel - 1);
264         }
265         if (source != null && !source.isAssignableFrom(rawType)) {
266             return null;
267         }
268         Class JavaDoc fromSuperclassOrInterface =
269                 extractTypeFromClass(methodParam, rawType, source, typeIndex, nestingLevel);
270         if (fromSuperclassOrInterface != null) {
271             return fromSuperclassOrInterface;
272         }
273         if (paramTypes == null || typeIndex >= paramTypes.length) {
274             return null;
275         }
276         Type JavaDoc paramType = paramTypes[typeIndex];
277         if (paramType instanceof WildcardType JavaDoc) {
278             Type JavaDoc[] lowerBounds = ((WildcardType JavaDoc) paramType).getLowerBounds();
279             if (lowerBounds != null && lowerBounds.length > 0) {
280                 paramType = lowerBounds[0];
281             }
282         }
283         if (paramType instanceof ParameterizedType JavaDoc) {
284             paramType = ((ParameterizedType JavaDoc) paramType).getRawType();
285         }
286         if (paramType instanceof GenericArrayType JavaDoc) {
287             // A generic array type... Let's turn it into a straight array type if possible.
288
Type JavaDoc compType = ((GenericArrayType JavaDoc) paramType).getGenericComponentType();
289             if (compType instanceof Class JavaDoc) {
290                 return Array.newInstance((Class JavaDoc) compType, 0).getClass();
291             }
292         }
293         else if (paramType instanceof Class JavaDoc) {
294             // We finally got a straight Class...
295
return (Class JavaDoc) paramType;
296         }
297         return null;
298     }
299
300     /**
301      * Extract the generic type from the given Class object.
302      * @param clazz the Class to check
303      * @param source the expected raw source type (can be <code>null</code>)
304      * @param typeIndex the index of the actual type argument
305      * @return the generic type as Class, or <code>null</code> if none
306      */

307     private static Class JavaDoc extractTypeFromClass(Class JavaDoc clazz, Class JavaDoc source, int typeIndex) {
308         return extractTypeFromClass(null, clazz, source, typeIndex, 1);
309     }
310
311     /**
312      * Extract the generic type from the given Class object.
313      * @param methodParam the method parameter specification
314      * @param clazz the Class to check
315      * @param source the expected raw source type (can be <code>null</code>)
316      * @param typeIndex the index of the actual type argument
317      * @param nestingLevel the nesting level of the target type
318      * @return the generic type as Class, or <code>null</code> if none
319      */

320     private static Class JavaDoc extractTypeFromClass(
321             MethodParameter methodParam, Class JavaDoc clazz, Class JavaDoc source, int typeIndex, int nestingLevel) {
322
323         Assert.notNull(clazz, "Class must not be null");
324         if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
325             return extractType(methodParam, clazz.getGenericSuperclass(), source, typeIndex, nestingLevel);
326         }
327         Type JavaDoc[] ifcs = clazz.getGenericInterfaces();
328         if (ifcs != null) {
329             for (int i = 0; i < ifcs.length; i++) {
330                 Type JavaDoc ifc = ifcs[i];
331                 Type JavaDoc rawType = ifc;
332                 if (ifc instanceof ParameterizedType JavaDoc) {
333                     rawType = ((ParameterizedType JavaDoc) ifc).getRawType();
334                 }
335                 if (rawType instanceof Class JavaDoc && isIntrospectionCandidate((Class JavaDoc) rawType)) {
336                     return extractType(methodParam, ifc, source, typeIndex, nestingLevel);
337                 }
338             }
339         }
340         return null;
341     }
342
343     /**
344      * Determine whether the given class is a potential candidate
345      * that defines generic collection or map types.
346      * @param clazz the class to check
347      * @return whethe the given class is assignable to Collection or Map
348      */

349     private static boolean isIntrospectionCandidate(Class JavaDoc clazz) {
350         return (Collection JavaDoc.class.isAssignableFrom(clazz) || Map JavaDoc.class.isAssignableFrom(clazz));
351     }
352
353 }
354
Popular Tags