Home » Spring-Framework-090522 » org.springframework » core » [javadoc | source]

    1   /*
    2    * Copyright 2002-2008 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;
   20   import java.lang.reflect.Field;
   21   import java.lang.reflect.GenericArrayType;
   22   import java.lang.reflect.Method;
   23   import java.lang.reflect.ParameterizedType;
   24   import java.lang.reflect.Type;
   25   import java.lang.reflect.TypeVariable;
   26   import java.lang.reflect.WildcardType;
   27   import java.util.Collection;
   28   import java.util.Map;
   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.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 getCollectionType(Class collectionClass) {
   54   		return extractTypeFromClass(collectionClass, Collection.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 getMapKeyType(Class mapClass) {
   64   		return extractTypeFromClass(mapClass, Map.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 getMapValueType(Class mapClass) {
   74   		return extractTypeFromClass(mapClass, Map.class, 1);
   75   	}
   76   
   77   	/**
   78   	 * Determine the generic element type of the given Collection field.
   79   	 * @param collectionField the collection field to introspect
   80   	 * @return the generic type, or <code>null</code> if none
   81   	 */
   82   	public static Class getCollectionFieldType(Field collectionField) {
   83   		return getGenericFieldType(collectionField, Collection.class, 0, 1);
   84   	}
   85   
   86   	/**
   87   	 * Determine the generic element type of the given Collection field.
   88   	 * @param collectionField the collection field to introspect
   89   	 * @param nestingLevel the nesting level of the target type
   90   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
   91   	 * nested List, whereas 2 would indicate the element of the nested List)
   92   	 * @return the generic type, or <code>null</code> if none
   93   	 */
   94   	public static Class getCollectionFieldType(Field collectionField, int nestingLevel) {
   95   		return getGenericFieldType(collectionField, Collection.class, 0, nestingLevel);
   96   	}
   97   
   98   	/**
   99   	 * Determine the generic key type of the given Map field.
  100   	 * @param mapField the map field to introspect
  101   	 * @return the generic type, or <code>null</code> if none
  102   	 */
  103   	public static Class getMapKeyFieldType(Field mapField) {
  104   		return getGenericFieldType(mapField, Map.class, 0, 1);
  105   	}
  106   
  107   	/**
  108   	 * Determine the generic key type of the given Map field.
  109   	 * @param mapField the map field to introspect
  110   	 * @param nestingLevel the nesting level of the target type
  111   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
  112   	 * nested List, whereas 2 would indicate the element of the nested List)
  113   	 * @return the generic type, or <code>null</code> if none
  114   	 */
  115   	public static Class getMapKeyFieldType(Field mapField, int nestingLevel) {
  116   		return getGenericFieldType(mapField, Map.class, 0, nestingLevel);
  117   	}
  118   
  119   	/**
  120   	 * Determine the generic value type of the given Map field.
  121   	 * @param mapField the map field to introspect
  122   	 * @return the generic type, or <code>null</code> if none
  123   	 */
  124   	public static Class getMapValueFieldType(Field mapField) {
  125   		return getGenericFieldType(mapField, Map.class, 1, 1);
  126   	}
  127   
  128   	/**
  129   	 * Determine the generic value type of the given Map field.
  130   	 * @param mapField the map field to introspect
  131   	 * @param nestingLevel the nesting level of the target type
  132   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
  133   	 * nested List, whereas 2 would indicate the element of the nested List)
  134   	 * @return the generic type, or <code>null</code> if none
  135   	 */
  136   	public static Class getMapValueFieldType(Field mapField, int nestingLevel) {
  137   		return getGenericFieldType(mapField, Map.class, 1, nestingLevel);
  138   	}
  139   
  140   	/**
  141   	 * Determine the generic element type of the given Collection parameter.
  142   	 * @param methodParam the method parameter specification
  143   	 * @return the generic type, or <code>null</code> if none
  144   	 */
  145   	public static Class getCollectionParameterType(MethodParameter methodParam) {
  146   		return getGenericParameterType(methodParam, Collection.class, 0);
  147   	}
  148   
  149   	/**
  150   	 * Determine the generic key type of the given Map parameter.
  151   	 * @param methodParam the method parameter specification
  152   	 * @return the generic type, or <code>null</code> if none
  153   	 */
  154   	public static Class getMapKeyParameterType(MethodParameter methodParam) {
  155   		return getGenericParameterType(methodParam, Map.class, 0);
  156   	}
  157   
  158   	/**
  159   	 * Determine the generic value type of the given Map parameter.
  160   	 * @param methodParam the method parameter specification
  161   	 * @return the generic type, or <code>null</code> if none
  162   	 */
  163   	public static Class getMapValueParameterType(MethodParameter methodParam) {
  164   		return getGenericParameterType(methodParam, Map.class, 1);
  165   	}
  166   
  167   	/**
  168   	 * Determine the generic element type of the given Collection return type.
  169   	 * @param method the method to check the return type for
  170   	 * @return the generic type, or <code>null</code> if none
  171   	 */
  172   	public static Class getCollectionReturnType(Method method) {
  173   		return getGenericReturnType(method, Collection.class, 0, 1);
  174   	}
  175   
  176   	/**
  177   	 * Determine the generic element type of the given Collection return type.
  178   	 * <p>If the specified nesting level is higher than 1, the element type of
  179   	 * a nested Collection/Map will be analyzed.
  180   	 * @param method the method to check the return type for
  181   	 * @param nestingLevel the nesting level of the target type
  182   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
  183   	 * nested List, whereas 2 would indicate the element of the nested List)
  184   	 * @return the generic type, or <code>null</code> if none
  185   	 */
  186   	public static Class getCollectionReturnType(Method method, int nestingLevel) {
  187   		return getGenericReturnType(method, Collection.class, 0, nestingLevel);
  188   	}
  189   
  190   	/**
  191   	 * Determine the generic key type of the given Map return type.
  192   	 * @param method the method to check the return type for
  193   	 * @return the generic type, or <code>null</code> if none
  194   	 */
  195   	public static Class getMapKeyReturnType(Method method) {
  196   		return getGenericReturnType(method, Map.class, 0, 1);
  197   	}
  198   
  199   	/**
  200   	 * Determine the generic key type of the given Map return type.
  201   	 * @param method the method to check the return type for
  202   	 * @param nestingLevel the nesting level of the target type
  203   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
  204   	 * nested List, whereas 2 would indicate the element of the nested List)
  205   	 * @return the generic type, or <code>null</code> if none
  206   	 */
  207   	public static Class getMapKeyReturnType(Method method, int nestingLevel) {
  208   		return getGenericReturnType(method, Map.class, 0, nestingLevel);
  209   	}
  210   
  211   	/**
  212   	 * Determine the generic value type of the given Map return type.
  213   	 * @param method the method to check the return type for
  214   	 * @return the generic type, or <code>null</code> if none
  215   	 */
  216   	public static Class getMapValueReturnType(Method method) {
  217   		return getGenericReturnType(method, Map.class, 1, 1);
  218   	}
  219   
  220   	/**
  221   	 * Determine the generic value type of the given Map return type.
  222   	 * @param method the method to check the return type for
  223   	 * @param nestingLevel the nesting level of the target type
  224   	 * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
  225   	 * nested List, whereas 2 would indicate the element of the nested List)
  226   	 * @return the generic type, or <code>null</code> if none
  227   	 */
  228   	public static Class getMapValueReturnType(Method method, int nestingLevel) {
  229   		return getGenericReturnType(method, Map.class, 1, nestingLevel);
  230   	}
  231   
  232   
  233   	/**
  234   	 * Extract the generic parameter type from the given method or constructor.
  235   	 * @param methodParam the method parameter specification
  236   	 * @param source the source class/interface defining the generic parameter types
  237   	 * @param typeIndex the index of the type (e.g. 0 for Collections,
  238   	 * 0 for Map keys, 1 for Map values)
  239   	 * @return the generic type, or <code>null</code> if none
  240   	 */
  241   	private static Class getGenericParameterType(MethodParameter methodParam, Class source, int typeIndex) {
  242   		return extractType(methodParam, GenericTypeResolver.getTargetType(methodParam),
  243   				source, typeIndex, methodParam.getNestingLevel(), 1);
  244   	}
  245   
  246   	/**
  247   	 * Extract the generic type from the given field.
  248   	 * @param field the field to check the type for
  249   	 * @param source the source class/interface defining the generic parameter types
  250   	 * @param typeIndex the index of the type (e.g. 0 for Collections,
  251   	 * 0 for Map keys, 1 for Map values)
  252   	 * @param nestingLevel the nesting level of the target type
  253   	 * @return the generic type, or <code>null</code> if none
  254   	 */
  255   	private static Class getGenericFieldType(Field field, Class source, int typeIndex, int nestingLevel) {
  256   		return extractType(null, field.getGenericType(), source, typeIndex, nestingLevel, 1);
  257   	}
  258   
  259   	/**
  260   	 * Extract the generic return type from the given method.
  261   	 * @param method the method to check the return type for
  262   	 * @param source the source class/interface defining the generic parameter types
  263   	 * @param typeIndex the index of the type (e.g. 0 for Collections,
  264   	 * 0 for Map keys, 1 for Map values)
  265   	 * @param nestingLevel the nesting level of the target type
  266   	 * @return the generic type, or <code>null</code> if none
  267   	 */
  268   	private static Class getGenericReturnType(Method method, Class source, int typeIndex, int nestingLevel) {
  269   		return extractType(null, method.getGenericReturnType(), source, typeIndex, nestingLevel, 1);
  270   	}
  271   
  272   	/**
  273   	 * Extract the generic type from the given Type object.
  274   	 * @param methodParam the method parameter specification
  275   	 * @param type the Type to check
  276   	 * @param source the source collection/map Class that we check
  277   	 * @param typeIndex the index of the actual type argument
  278   	 * @param nestingLevel the nesting level of the target type
  279   	 * @param currentLevel the current nested level
  280   	 * @return the generic type as Class, or <code>null</code> if none
  281   	 */
  282   	private static Class extractType(
  283   			MethodParameter methodParam, Type type, Class source, int typeIndex, int nestingLevel, int currentLevel) {
  284   
  285   		Type resolvedType = type;
  286   		if (type instanceof TypeVariable && methodParam != null && methodParam.typeVariableMap != null) {
  287   			Type mappedType = (Type) methodParam.typeVariableMap.get(type);
  288   			if (mappedType != null) {
  289   				resolvedType = mappedType;
  290   			}
  291   		}
  292   		if (resolvedType instanceof ParameterizedType) {
  293   			return extractTypeFromParameterizedType(
  294   					methodParam, (ParameterizedType) resolvedType, source, typeIndex, nestingLevel, currentLevel);
  295   		}
  296   		else if (resolvedType instanceof Class) {
  297   			return extractTypeFromClass(methodParam, (Class) resolvedType, source, typeIndex, nestingLevel, currentLevel);
  298   		}
  299   		else {
  300   			return null;
  301   		}
  302   	}
  303   
  304   	/**
  305   	 * Extract the generic type from the given ParameterizedType object.
  306   	 * @param methodParam the method parameter specification
  307   	 * @param ptype the ParameterizedType to check
  308   	 * @param source the expected raw source type (can be <code>null</code>)
  309   	 * @param typeIndex the index of the actual type argument
  310   	 * @param nestingLevel the nesting level of the target type
  311   	 * @param currentLevel the current nested level
  312   	 * @return the generic type as Class, or <code>null</code> if none
  313   	 */
  314   	private static Class extractTypeFromParameterizedType(MethodParameter methodParam,
  315   			ParameterizedType ptype, Class source, int typeIndex, int nestingLevel, int currentLevel) {
  316   
  317   		if (!(ptype.getRawType() instanceof Class)) {
  318   			return null;
  319   		}
  320   		Class rawType = (Class) ptype.getRawType();
  321   		Type[] paramTypes = ptype.getActualTypeArguments();
  322   		if (nestingLevel - currentLevel > 0) {
  323   			int nextLevel = currentLevel + 1;
  324   			Integer currentTypeIndex = (methodParam != null ? methodParam.getTypeIndexForLevel(nextLevel) : null);
  325   			// Default is last parameter type: Collection element or Map value.
  326   			int indexToUse = (currentTypeIndex != null ? currentTypeIndex.intValue() : paramTypes.length - 1);
  327   			Type paramType = paramTypes[indexToUse];
  328   			return extractType(methodParam, paramType, source, typeIndex, nestingLevel, nextLevel);
  329   		}
  330   		if (source != null && !source.isAssignableFrom(rawType)) {
  331   			return null;
  332   		}
  333   		Class fromSuperclassOrInterface =
  334   				extractTypeFromClass(methodParam, rawType, source, typeIndex, nestingLevel, currentLevel);
  335   		if (fromSuperclassOrInterface != null) {
  336   			return fromSuperclassOrInterface;
  337   		}
  338   		if (paramTypes == null || typeIndex >= paramTypes.length) {
  339   			return null;
  340   		}
  341   		Type paramType = paramTypes[typeIndex];
  342   		if (paramType instanceof TypeVariable && methodParam != null && methodParam.typeVariableMap != null) {
  343   			Type mappedType = (Type) methodParam.typeVariableMap.get(paramType);
  344   			if (mappedType != null) {
  345   				paramType = mappedType;
  346   			}
  347   		}
  348   		if (paramType instanceof WildcardType) {
  349   			Type[] lowerBounds = ((WildcardType) paramType).getLowerBounds();
  350   			if (lowerBounds != null && lowerBounds.length > 0) {
  351   				paramType = lowerBounds[0];
  352   			}
  353   		}
  354   		if (paramType instanceof ParameterizedType) {
  355   			paramType = ((ParameterizedType) paramType).getRawType();
  356   		}
  357   		if (paramType instanceof GenericArrayType) {
  358   			// A generic array type... Let's turn it into a straight array type if possible.
  359   			Type compType = ((GenericArrayType) paramType).getGenericComponentType();
  360   			if (compType instanceof Class) {
  361   				return Array.newInstance((Class) compType, 0).getClass();
  362   			}
  363   		}
  364   		else if (paramType instanceof Class) {
  365   			// We finally got a straight Class...
  366   			return (Class) paramType;
  367   		}
  368   		return null;
  369   	}
  370   
  371   	/**
  372   	 * Extract the generic type from the given Class object.
  373   	 * @param clazz the Class to check
  374   	 * @param source the expected raw source type (can be <code>null</code>)
  375   	 * @param typeIndex the index of the actual type argument
  376   	 * @return the generic type as Class, or <code>null</code> if none
  377   	 */
  378   	private static Class extractTypeFromClass(Class clazz, Class source, int typeIndex) {
  379   		return extractTypeFromClass(null, clazz, source, typeIndex, 1, 1);
  380   	}
  381   
  382   	/**
  383   	 * Extract the generic type from the given Class object.
  384   	 * @param methodParam the method parameter specification
  385   	 * @param clazz the Class to check
  386   	 * @param source the expected raw source type (can be <code>null</code>)
  387   	 * @param typeIndex the index of the actual type argument
  388   	 * @param nestingLevel the nesting level of the target type
  389   	 * @param currentLevel the current nested level
  390   	 * @return the generic type as Class, or <code>null</code> if none
  391   	 */
  392   	private static Class extractTypeFromClass(
  393   			MethodParameter methodParam, Class clazz, Class source, int typeIndex, int nestingLevel, int currentLevel) {
  394   
  395   		if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
  396   			return extractType(methodParam, clazz.getGenericSuperclass(), source, typeIndex, nestingLevel, currentLevel);
  397   		}
  398   		Type[] ifcs = clazz.getGenericInterfaces();
  399   		if (ifcs != null) {
  400   			for (int i = 0; i < ifcs.length; i++) {
  401   				Type ifc = ifcs[i];
  402   				Type rawType = ifc;
  403   				if (ifc instanceof ParameterizedType) {
  404   					rawType = ((ParameterizedType) ifc).getRawType();
  405   				}
  406   				if (rawType instanceof Class && isIntrospectionCandidate((Class) rawType)) {
  407   					return extractType(methodParam, ifc, source, typeIndex, nestingLevel, currentLevel);
  408   				}
  409   			}
  410   		}
  411   		return null;
  412   	}
  413   
  414   	/**
  415   	 * Determine whether the given class is a potential candidate
  416   	 * that defines generic collection or map types.
  417   	 * @param clazz the class to check
  418   	 * @return whethe the given class is assignable to Collection or Map
  419   	 */
  420   	private static boolean isIntrospectionCandidate(Class clazz) {
  421   		return (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz));
  422   	}
  423   
  424   }

Home » Spring-Framework-090522 » org.springframework » core » [javadoc | source]