Skip to content

Instantly share code, notes, and snippets.

@HaloFour
Created June 6, 2021 16:16
Show Gist options
  • Save HaloFour/ef61fdab146536f0f1f8969a4a301404 to your computer and use it in GitHub Desktop.
Save HaloFour/ef61fdab146536f0f1f8969a4a301404 to your computer and use it in GitHub Desktop.
Helper class for resolving actual type arguments of a generic superclass
/*
* 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