Created
June 6, 2021 16:16
-
-
Save HaloFour/ef61fdab146536f0f1f8969a4a301404 to your computer and use it in GitHub Desktop.
Helper class for resolving actual type arguments of a generic superclass
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright The OpenTelemetry Authors | |
* SPDX-License-Identifier: Apache-2.0 | |
*/ | |
package io.opentelemetry.instrumentation.api.tracer; | |
import java.lang.reflect.ParameterizedType; | |
import java.lang.reflect.Type; | |
import java.lang.reflect.TypeVariable; | |
import java.util.HashSet; | |
import java.util.Objects; | |
import java.util.Set; | |
/** | |
* Helper class for resolving the actual type arguments applied to the type parameters of a generic | |
* class. | |
*/ | |
class TypeArgumentResolver { | |
private final Class<?> genericClass; | |
private final Set<Type> visited = new HashSet<>(); | |
public static Type[] resolveActualTypeArguments(Type type, Class<?> genericClass) { | |
Objects.requireNonNull(type); | |
Objects.requireNonNull(genericClass); | |
TypeArgumentResolver resolver = new TypeArgumentResolver(genericClass); | |
Type[] typeArguments = resolver.resolve(type); | |
if (typeArguments == null) { | |
throw new IllegalArgumentException( | |
String.format( | |
"Type %s does not extend from or implement generic class %s", type, genericClass)); | |
} | |
return typeArguments; | |
} | |
private TypeArgumentResolver(Class<?> genericClass) { | |
this.genericClass = genericClass; | |
} | |
private Type[] resolve(Type type) { | |
if (type != null && visited.add(type)) { | |
try { | |
if (type instanceof Class) { | |
return resolve((Class<?>) type); | |
} else if (type instanceof ParameterizedType) { | |
return resolve((ParameterizedType) type); | |
} | |
} finally { | |
visited.remove(type); | |
} | |
} | |
return null; | |
} | |
private Type[] resolve(Type[] types) { | |
Type[] typeArguments = null; | |
for (Type type : types) { | |
typeArguments = resolve(type); | |
if (typeArguments != null) { | |
break; | |
} | |
} | |
return typeArguments; | |
} | |
private Type[] resolve(Class<?> cls) { | |
if (cls == genericClass) { | |
return cls.getTypeParameters(); | |
} | |
Type[] typeArguments = resolve(cls.getGenericSuperclass()); | |
if (typeArguments == null) { | |
typeArguments = resolve(cls.getGenericInterfaces()); | |
} | |
return typeArguments; | |
} | |
private Type[] resolve(ParameterizedType parameterizedType) { | |
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); | |
Class<?> rawClass = (Class<?>) parameterizedType.getRawType(); | |
if (rawClass == genericClass) { | |
return actualTypeArguments; | |
} | |
return resolve(rawClass, actualTypeArguments); | |
} | |
private Type[] resolve(Class<?> rawClass, Type[] actualTypeArguments) { | |
Type[] typeArguments = resolve(rawClass); | |
if (typeArguments == null) { | |
return null; | |
} | |
return mapTypeVariablesToActualTypeArguments( | |
typeArguments, rawClass.getTypeParameters(), actualTypeArguments); | |
} | |
private static Type[] mapTypeVariablesToActualTypeArguments( | |
Type[] typeArguments, TypeVariable<?>[] typeParameters, Type[] actualTypeArguments) { | |
for (int i = 0; i < typeArguments.length; i++) { | |
Type typeArgument = typeArguments[i]; | |
if (typeArgument instanceof TypeVariable) { | |
typeArguments[i] = | |
mapTypeVariableToActualTypeArgument( | |
(TypeVariable<?>) typeArgument, typeParameters, actualTypeArguments); | |
} | |
} | |
return typeArguments; | |
} | |
private static Type mapTypeVariableToActualTypeArgument( | |
TypeVariable<?> typeVariable, TypeVariable<?>[] typeParameters, Type[] actualTypeArguments) { | |
for (int i = 0; i < typeParameters.length; i++) { | |
if (typeVariableEquals(typeVariable, typeParameters[i])) { | |
return actualTypeArguments[i]; | |
} | |
} | |
return typeVariable; | |
} | |
private static boolean typeVariableEquals(TypeVariable<?> left, TypeVariable<?> right) { | |
return left.getGenericDeclaration().equals(right.getGenericDeclaration()) | |
&& left.getName().equals(right.getName()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment