diff options
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/utils')
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt index f714ebc97..cc6251a17 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt @@ -3,6 +3,10 @@ package at.hannibal2.skyhanni.utils import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Modifier +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.lang.reflect.TypeVariable +import java.lang.reflect.WildcardType import kotlin.properties.ReadWriteProperty import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty @@ -65,4 +69,65 @@ object ReflectionUtils { } fun Class<*>.getDeclaredFieldOrNull(name: String): Field? = declaredFields.firstOrNull { it.name == name } + + + /** + * Resolve all super class generic type parameters to their respective bound types in the class inheriting them. + * Note that this is only done once, so a class declaration like + * ```kotlin + * class Parent<ParentT> + * class Child<OtherT> : Parent<OtherT> + * class GrandChild : Child<String> + * ``` + * would result in `mapOf(OtherT to String, OtherT to ParentT)`. Variables bound to variables need to be manually unraveled. + * Note also that wild cards like + * ```kotlin + * class WildChild : Parent<out String> + * ``` + * are left untouched: `mapOf(ParentT to WildCardType(arrayOf(String), arrayOf()))` + */ + fun findSuperClassTypeParameters( + type: Type?, + universe: MutableMap<TypeVariable<*>, Type>, // TODO: this could go with a (owner, name) tuple key instead + ) { + when (type) { + is ParameterizedType -> { + val rawType = type.rawType as Class<*> // TODO check + rawType.typeParameters.zip(type.actualTypeArguments).associateTo(universe) { it } + findSuperClassTypeParameters(rawType.genericSuperclass, universe) + } + + is Class<*> -> { + findSuperClassTypeParameters(type.genericSuperclass, universe) + } + + is TypeVariable<*> -> { + findSuperClassTypeParameters(universe[type] ?: return, universe) + } + } + } + + /** + * Resolve the upper bound of a type variable from a child classes type parameters using [findSuperClassTypeParameters]. + * + * This method performs the mentioned resolving of type parameters and wild card resolutions. + * Note that the returned class may not actually be allowed by all bounds along the chain, so might be a super class of + * what you would expect. + */ + fun resolveUpperBoundSuperClassGenericParameter(type: Type, variable: TypeVariable<*>): Class<*>? { + val universe = mutableMapOf<TypeVariable<*>, Type>() + findSuperClassTypeParameters(type, universe) + var p: Type = variable + while (true) { + if (p is TypeVariable<*>) { + p = universe[p] ?: return null + } else if (p is WildcardType) { + p = p.upperBounds[0] + } else if (p is Class<*>) { + return p + } else { + return null + } + } + } } |