All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.core.GenericTypeResolver Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;

/**
 * Helper class for resolving generic types against type variables.
 *
 * 

Mainly intended for usage within the framework, resolving method * parameter types even when they are declared generically. * * @author Juergen Hoeller * @author Rob Harrop * @author Sam Brannen * @author Phillip Webb * @since 2.5.2 */ public final class GenericTypeResolver { /** Cache from Class to TypeVariable Map. */ @SuppressWarnings("rawtypes") private static final Map, Map> typeVariableCache = new ConcurrentReferenceHashMap<>(); private GenericTypeResolver() { } /** * Determine the target type for the given generic parameter type. * @param methodParameter the method parameter specification * @param implementationClass the class to resolve type variables against * @return the corresponding generic parameter or return type * @deprecated since 5.2 in favor of {@code methodParameter.withContainingClass(implementationClass).getParameterType()} */ @Deprecated public static Class resolveParameterType(MethodParameter methodParameter, Class implementationClass) { Assert.notNull(methodParameter, "MethodParameter must not be null"); Assert.notNull(implementationClass, "Class must not be null"); methodParameter.setContainingClass(implementationClass); return methodParameter.getParameterType(); } /** * Determine the target type for the generic return type of the given method, * where formal type variables are declared on the given class. * @param method the method to introspect * @param clazz the class to resolve type variables against * @return the corresponding generic parameter or return type */ public static Class resolveReturnType(Method method, Class clazz) { Assert.notNull(method, "Method must not be null"); Assert.notNull(clazz, "Class must not be null"); return ResolvableType.forMethodReturnType(method, clazz).resolve(method.getReturnType()); } /** * Resolve the single type argument of the given generic interface against the given * target method which is assumed to return the given interface or an implementation * of it. * @param method the target method to check the return type of * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved parameter type of the method return type, or {@code null} * if not resolvable or if the single argument is of type {@link WildcardType}. */ @Nullable public static Class resolveReturnTypeArgument(Method method, Class genericIfc) { Assert.notNull(method, "Method must not be null"); ResolvableType resolvableType = ResolvableType.forMethodReturnType(method).as(genericIfc); if (!resolvableType.hasGenerics() || resolvableType.getType() instanceof WildcardType) { return null; } return getSingleGeneric(resolvableType); } /** * Resolve the single type argument of the given generic interface against * the given target class which is assumed to implement the generic interface * and possibly declare a concrete type for its type variable. * @param clazz the target class to check against * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of the argument, or {@code null} if not resolvable */ @Nullable public static Class resolveTypeArgument(Class clazz, Class genericIfc) { ResolvableType resolvableType = ResolvableType.forClass(clazz).as(genericIfc); if (!resolvableType.hasGenerics()) { return null; } return getSingleGeneric(resolvableType); } @Nullable private static Class getSingleGeneric(ResolvableType resolvableType) { Assert.isTrue(resolvableType.getGenerics().length == 1, () -> "Expected 1 type argument on generic interface [" + resolvableType + "] but found " + resolvableType.getGenerics().length); return resolvableType.getGeneric().resolve(); } /** * Resolve the type arguments of the given generic interface against the given * target class which is assumed to implement the generic interface and possibly * declare concrete types for its type variables. * @param clazz the target class to check against * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of each argument, with the array size matching the * number of actual type arguments, or {@code null} if not resolvable */ @Nullable public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc); if (!type.hasGenerics() || type.isEntirelyUnresolvable()) { return null; } return type.resolveGenerics(Object.class); } /** * Resolve the given generic type against the given context class, * substituting type variables as far as possible. * @param genericType the (potentially) generic type * @param contextClass a context class for the target type, for example a class * in which the target type appears in a method signature (can be {@code null}) * @return the resolved type (possibly the given generic type as-is) * @since 5.0 */ public static Type resolveType(Type genericType, @Nullable Class contextClass) { if (contextClass != null) { if (genericType instanceof TypeVariable typeVariable) { ResolvableType resolvedTypeVariable = resolveVariable( typeVariable, ResolvableType.forClass(contextClass)); if (resolvedTypeVariable != ResolvableType.NONE) { Class resolved = resolvedTypeVariable.resolve(); if (resolved != null) { return resolved; } } } else if (genericType instanceof ParameterizedType parameterizedType) { ResolvableType resolvedType = ResolvableType.forType(genericType); if (resolvedType.hasUnresolvableGenerics()) { Class[] generics = new Class[parameterizedType.getActualTypeArguments().length]; Type[] typeArguments = parameterizedType.getActualTypeArguments(); ResolvableType contextType = ResolvableType.forClass(contextClass); for (int i = 0; i < typeArguments.length; i++) { Type typeArgument = typeArguments[i]; if (typeArgument instanceof TypeVariable typeVariable) { ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType); if (resolvedTypeArgument != ResolvableType.NONE) { generics[i] = resolvedTypeArgument.resolve(); } else { generics[i] = ResolvableType.forType(typeArgument).resolve(); } } else { generics[i] = ResolvableType.forType(typeArgument).resolve(); } } Class rawClass = resolvedType.getRawClass(); if (rawClass != null) { return ResolvableType.forClassWithGenerics(rawClass, generics).getType(); } } } } return genericType; } private static ResolvableType resolveVariable(TypeVariable typeVariable, ResolvableType contextType) { ResolvableType resolvedType; if (contextType.hasGenerics()) { ResolvableType.VariableResolver variableResolver = contextType.asVariableResolver(); if (variableResolver == null) { return ResolvableType.NONE; } resolvedType = variableResolver.resolveVariable(typeVariable); if (resolvedType != null) { return resolvedType; } } ResolvableType superType = contextType.getSuperType(); if (superType != ResolvableType.NONE) { resolvedType = resolveVariable(typeVariable, superType); if (resolvedType != ResolvableType.NONE) { return resolvedType; } } for (ResolvableType ifc : contextType.getInterfaces()) { resolvedType = resolveVariable(typeVariable, ifc); if (resolvedType != ResolvableType.NONE) { return resolvedType; } } return ResolvableType.NONE; } /** * Resolve the specified generic type against the given TypeVariable map. *

Used by Spring Data. * @param genericType the generic type to resolve * @param map the TypeVariable Map to resolved against * @return the type if it resolves to a Class, or {@code Object.class} otherwise */ @SuppressWarnings("rawtypes") public static Class resolveType(Type genericType, Map map) { return ResolvableType.forType(genericType, new TypeVariableMapVariableResolver(map)).toClass(); } /** * Build a mapping of {@link TypeVariable#getName TypeVariable names} to * {@link Class concrete classes} for the specified {@link Class}. * Searches all supertypes, enclosing types and interfaces. * @see #resolveType(Type, Map) */ @SuppressWarnings("rawtypes") public static Map getTypeVariableMap(Class clazz) { Map typeVariableMap = typeVariableCache.get(clazz); if (typeVariableMap == null) { typeVariableMap = new HashMap<>(); buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap); typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap)); } return typeVariableMap; } @SuppressWarnings("rawtypes") private static void buildTypeVariableMap(ResolvableType type, Map typeVariableMap) { if (type != ResolvableType.NONE) { Class resolved = type.resolve(); if (resolved != null && type.getType() instanceof ParameterizedType) { TypeVariable[] variables = resolved.getTypeParameters(); for (int i = 0; i < variables.length; i++) { ResolvableType generic = type.getGeneric(i); while (generic.getType() instanceof TypeVariable) { generic = generic.resolveType(); } if (generic != ResolvableType.NONE) { typeVariableMap.put(variables[i], generic.getType()); } } } buildTypeVariableMap(type.getSuperType(), typeVariableMap); for (ResolvableType interfaceType : type.getInterfaces()) { buildTypeVariableMap(interfaceType, typeVariableMap); } if (resolved != null && resolved.isMemberClass()) { buildTypeVariableMap(ResolvableType.forClass(resolved.getEnclosingClass()), typeVariableMap); } } } @SuppressWarnings({"serial", "rawtypes"}) private static class TypeVariableMapVariableResolver implements ResolvableType.VariableResolver { private final Map typeVariableMap; public TypeVariableMapVariableResolver(Map typeVariableMap) { this.typeVariableMap = typeVariableMap; } @Override @Nullable public ResolvableType resolveVariable(TypeVariable variable) { Type type = this.typeVariableMap.get(variable); return (type != null ? ResolvableType.forType(type) : null); } @Override public Object getSource() { return this.typeVariableMap; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy