From dffbc34bac795b00ca6626253c598ee8984030bd Mon Sep 17 00:00:00 2001 From: Oryan Date: Fri, 9 Oct 2026 18:04:12 -0400 Subject: [PATCH 1/3] Drop unused declaring type from GenericType Since type variables are resolved against the most specific type, the declaring type was only used in an error message. Fold RelativeTo into GenericType and stop passing a declaring type from the field resolvers. Co-Authored-By: Claude Opus 5.5 --- .../graphql/kickstart/tools/GenericType.kt | 168 ++++++------------ .../kickstart/tools/SchemaClassScanner.kt | 2 +- .../kickstart/tools/TypeClassMatcher.kt | 6 +- .../kickstart/tools/resolver/FieldResolver.kt | 6 +- .../tools/resolver/MapFieldResolver.kt | 6 +- .../tools/resolver/MethodFieldResolver.kt | 2 +- .../tools/resolver/MissingFieldResolver.kt | 2 +- .../tools/resolver/PropertyFieldResolver.kt | 2 +- 8 files changed, 68 insertions(+), 126 deletions(-) diff --git a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt index a616ee2f..150d192f 100644 --- a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt +++ b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt @@ -12,7 +12,7 @@ import java.lang.reflect.WildcardType /** * @author Andrew Potter */ -internal open class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) { +internal class GenericType(private val mostSpecificType: JavaType, private val options: SchemaParserOptions) { fun isTypeAssignableFromRawClass(type: ParameterizedType, clazz: Class<*>) = clazz.isAssignableFrom(getRawClass(type.rawType)) @@ -23,125 +23,69 @@ internal open class GenericType(protected val mostSpecificType: JavaType, protec fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, mostSpecificType) - fun relativeToPotentialParent(declaringType: JavaType): RelativeTo { - if (declaringType !is Class<*> || declaringType.isInterface) { - return relativeToType(declaringType) - } - - val type = getGenericSuperType(mostSpecificType, declaringType) - if (type == null) { - error("Unable to find generic type of class ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}") - } else { - return relativeToType(type) - } - } - - fun relativeToType(declaringType: JavaType) = RelativeTo(declaringType, mostSpecificType, options) - - fun getGenericInterface(targetInterface: Class<*>) = getGenericInterface(mostSpecificType, targetInterface) - - private fun getGenericInterface(type: JavaType?, targetInterface: Class<*>): JavaType? { - if (type == null) { - return null - } - - val raw = type as? Class<*> ?: getRawClass(type) - - if (raw == targetInterface) { - return type - } - - val possibleSubInterface = raw.genericInterfaces.find { genericInterface -> - TypeUtils.isAssignable(genericInterface, targetInterface) - } ?: raw.interfaces.find { iface -> - TypeUtils.isAssignable(iface, targetInterface) - } ?: getGenericInterface(raw.genericSuperclass, targetInterface) ?: return null - - return getGenericInterface(possibleSubInterface, targetInterface) - } - - fun getGenericSuperType(targetSuperClass: Class<*>) = getGenericSuperType(mostSpecificType, targetSuperClass) - - private fun getGenericSuperType(type: JavaType?, targetSuperClass: Class<*>): JavaType? { - if (type == null) { - return null - } - - val raw = type as? Class<*> ?: TypeUtils.getRawType(type, type) + /** + * Unwrap certain Java types to find the "real" class. + */ + fun unwrapGenericType(javaType: JavaType): JavaType { + return when (val type = replaceTypeVariable(javaType)) { + is ParameterizedType -> { + val rawType = type.rawType + val genericType = options.genericWrappers.find { it.type == rawType } + ?: return type + + val typeArguments = type.actualTypeArguments + if (typeArguments.size <= genericType.index) { + throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!") + } - if (raw == targetSuperClass) { - return type + val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index]) + unwrapGenericType(unwrapsTo) + } + is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' relative to ${TypeUtils.toString(mostSpecificType)}") + is WildcardType -> type.upperBounds.firstOrNull() + ?: error("Unable to unwrap type, wildcard has no upper bound: $type") + is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type + else -> error("Unable to unwrap type: $type") } - - return getGenericSuperType(raw.genericSuperclass, targetSuperClass) } - class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions) : GenericType(mostSpecificType, options) { - - /** - * Unwrap certain Java types to find the "real" class. - */ - fun unwrapGenericType(javaType: JavaType): JavaType { - return when (val type = replaceTypeVariable(javaType)) { - is ParameterizedType -> { - val rawType = type.rawType - val genericType = options.genericWrappers.find { it.type == rawType } - ?: return type - - val typeArguments = type.actualTypeArguments - if (typeArguments.size <= genericType.index) { - throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!") - } - - val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index]) - unwrapGenericType(unwrapsTo) - } - is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' of ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}") - is WildcardType -> type.upperBounds.firstOrNull() - ?: error("Unable to unwrap type, wildcard has no upper bound: $type") - is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type - else -> error("Unable to unwrap type: $type") + private fun replaceTypeVariable(type: JavaType, resolving: Set> = emptySet()): JavaType { + return when (type) { + is ParameterizedType -> { + val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it, resolving) }.toTypedArray() + ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { replaceTypeVariable(it, resolving) }) } - } - - private fun replaceTypeVariable(type: JavaType, resolving: Set> = emptySet()): JavaType { - return when (type) { - is ParameterizedType -> { - val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it, resolving) }.toTypedArray() - ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { replaceTypeVariable(it, resolving) }) - } - is WildcardType -> TypeUtils.wildcardType() - .withUpperBounds(*type.upperBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) - .withLowerBounds(*type.lowerBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) - .build() - is ResolvedType -> { - if (type.typeParameters.isEmpty()) { - type.erasedType - } else { - val actualTypeArguments = type.typeParameters.map { replaceTypeVariable(it) }.toTypedArray() - ParameterizedTypeImpl(type.erasedType, actualTypeArguments, null) - } + is WildcardType -> TypeUtils.wildcardType() + .withUpperBounds(*type.upperBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) + .withLowerBounds(*type.lowerBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) + .build() + is ResolvedType -> { + if (type.typeParameters.isEmpty()) { + type.erasedType + } else { + val actualTypeArguments = type.typeParameters.map { replaceTypeVariable(it) }.toTypedArray() + ParameterizedTypeImpl(type.erasedType, actualTypeArguments, null) } - is TypeVariable<*> -> { - val genericDeclaration = type.genericDeclaration - when { - // only a variable leaked from a raw type can be bound to a type containing itself (e.g. T -> List), - // erase it like the raw type does instead of expanding it forever - type in resolving -> TypeUtils.getRawType(type.bounds.first(), null) ?: Any::class.java - // the most specific type binds the variables of all its supertypes - genericDeclaration is Class<*> -> generateSequence(mostSpecificType) { (it as? ParameterizedType)?.ownerType } - // an inner class can also use the variables of its outer class, those are bound by its owner type (e.g. Connection.Entry) - .firstNotNullOfOrNull { TypeUtils.getTypeArguments(it, genericDeclaration)?.get(type) } - // keep the full type argument (e.g. List) rather than its raw class so nested generics aren't lost - ?.let { replaceTypeVariable(it, resolving + type) } - ?: type - else -> type - } - } - else -> { - type + } + is TypeVariable<*> -> { + val genericDeclaration = type.genericDeclaration + when { + // only a variable leaked from a raw type can be bound to a type containing itself (e.g. T -> List), + // erase it like the raw type does instead of expanding it forever + type in resolving -> TypeUtils.getRawType(type.bounds.first(), null) ?: Any::class.java + // the most specific type binds the variables of all its supertypes + genericDeclaration is Class<*> -> generateSequence(mostSpecificType) { (it as? ParameterizedType)?.ownerType } + // an inner class can also use the variables of its outer class, those are bound by its owner type (e.g. Connection.Entry) + .firstNotNullOfOrNull { TypeUtils.getTypeArguments(it, genericDeclaration)?.get(type) } + // keep the full type argument (e.g. List) rather than its raw class so nested generics aren't lost + ?.let { replaceTypeVariable(it, resolving + type) } + ?: type + else -> type } } + else -> { + type + } } } } diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt index bea11ace..b3b47452 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt @@ -396,7 +396,7 @@ internal class SchemaClassScanner( handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType( inputValueDefinition.type, inputValueJavaType, - GenericType(javaType, options).relativeToType(javaType), + GenericType(javaType, options), InputObjectReference(inputValueDefinition) ))) } else { diff --git a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt index b505b16d..5497c8b2 100644 --- a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt +++ b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt @@ -109,15 +109,15 @@ internal class TypeClassMatcher(private val definitionsByName: Map diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt index e2b635be..2e135a32 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt @@ -21,10 +21,10 @@ internal class MapFieldResolver( field: FieldDefinition, search: FieldResolverScanner.Search, options: SchemaParserOptions, - relativeTo: JavaType -) : FieldResolver(field, search, options, relativeTo) { + mapClass: JavaType +) : FieldResolver(field, search, options) { - private var mapGenericValue: JavaType = getMapGenericType(relativeTo) + private var mapGenericValue: JavaType = getMapGenericType(mapClass) /** * Takes a type which implements Map and tries to find the diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt index 360382b8..86889677 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt @@ -41,7 +41,7 @@ internal class MethodFieldResolver( search: FieldResolverScanner.Search, options: SchemaParserOptions, val method: Method -) : FieldResolver(field, search, options, search.type) { +) : FieldResolver(field, search, options) { private val log = LoggerFactory.getLogger(javaClass) diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt index 74ed5a75..0061b811 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt @@ -9,7 +9,7 @@ import graphql.schema.DataFetcher internal class MissingFieldResolver( field: FieldDefinition, options: SchemaParserOptions -) : FieldResolver(field, FieldResolverScanner.Search(Any::class.java, MissingResolverInfo(), null), options, Any::class.java) { +) : FieldResolver(field, FieldResolverScanner.Search(Any::class.java, MissingResolverInfo(), null), options) { private val missingResolverDataFetcherProvider: MissingResolverDataFetcherProvider = options.missingResolverDataFetcherProvider ?: MissingResolverDataFetcherProvider { diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt index 6151181a..4093fe1a 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt @@ -19,7 +19,7 @@ internal class PropertyFieldResolver( search: FieldResolverScanner.Search, options: SchemaParserOptions, private val property: Field -) : FieldResolver(field, search, options, property.declaringClass) { +) : FieldResolver(field, search, options) { override fun createDataFetcher(): DataFetcher<*> { return PropertyFieldResolverDataFetcher(createSourceResolver(), property) From 9873b974bf62cd8d2070578ca585477b145ab0c3 Mon Sep 17 00:00:00 2001 From: Oryan Date: Sat, 10 Oct 2026 16:40:52 -0400 Subject: [PATCH 2/3] Use isSubtypeOf for generic wrapper type checks - Add ParameterizedType.isSubtypeOf and use it in TypeClassMatcher in place of GenericType.isTypeAssignableFromRawClass - Remove the isListType helpers in favor of the same check - Rename mostSpecificType to containingType and document unwrapGenericType --- .../graphql/kickstart/tools/GenericType.kt | 53 ++++++++++--------- .../kickstart/tools/TypeClassMatcher.kt | 17 +++--- .../graphql/kickstart/tools/util/Utils.kt | 2 + 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt index 150d192f..4b442085 100644 --- a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt +++ b/src/main/kotlin/graphql/kickstart/tools/GenericType.kt @@ -12,36 +12,39 @@ import java.lang.reflect.WildcardType /** * @author Andrew Potter */ -internal class GenericType(private val mostSpecificType: JavaType, private val options: SchemaParserOptions) { +internal class GenericType(private val containingType: JavaType, private val options: SchemaParserOptions) { - fun isTypeAssignableFromRawClass(type: ParameterizedType, clazz: Class<*>) = - clazz.isAssignableFrom(getRawClass(type.rawType)) + fun getRawClass() = getRawClass(containingType) - fun getRawClass() = getRawClass(mostSpecificType) + fun getRawClass(type: JavaType): Class<*> = TypeUtils.getRawType(type, containingType) - fun getRawClass(type: JavaType): Class<*> = TypeUtils.getRawType(type, mostSpecificType) - - fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, mostSpecificType) + fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, containingType) /** - * Unwrap certain Java types to find the "real" class. + * Turns a type as declared in the source (a method's return or parameter type, or a field's type) into the type + * the GraphQL type is matched against: + * - type variables are resolved relative to the containing type, + * e.g. `T` of `AbstractItem.getId()` becomes `Long` for `class Item : AbstractItem()` + * - generic wrappers are replaced by their wrapped type, e.g. `CompletableFuture` becomes `Foo` + * - wildcards are replaced by their upper bound, e.g. `? extends Foo` becomes `Foo` + * - primitives are replaced by their boxed class, e.g. `int` becomes `Integer` */ fun unwrapGenericType(javaType: JavaType): JavaType { - return when (val type = replaceTypeVariable(javaType)) { + return when (val type = resolveTypeVariables(javaType)) { is ParameterizedType -> { val rawType = type.rawType - val genericType = options.genericWrappers.find { it.type == rawType } + val wrapper = options.genericWrappers.find { it.type == rawType } ?: return type val typeArguments = type.actualTypeArguments - if (typeArguments.size <= genericType.index) { - throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!") + if (typeArguments.size <= wrapper.index) { + throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${wrapper.index}!") } - val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index]) + val unwrapsTo = wrapper.schemaWrapper.invoke(typeArguments[wrapper.index]) unwrapGenericType(unwrapsTo) } - is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' relative to ${TypeUtils.toString(mostSpecificType)}") + is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' relative to ${TypeUtils.toString(containingType)}") is WildcardType -> type.upperBounds.firstOrNull() ?: error("Unable to unwrap type, wildcard has no upper bound: $type") is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type @@ -49,21 +52,21 @@ internal class GenericType(private val mostSpecificType: JavaType, private val o } } - private fun replaceTypeVariable(type: JavaType, resolving: Set> = emptySet()): JavaType { + private fun resolveTypeVariables(type: JavaType, resolving: Set> = emptySet()): JavaType { return when (type) { is ParameterizedType -> { - val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it, resolving) }.toTypedArray() - ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { replaceTypeVariable(it, resolving) }) + val actualTypeArguments = type.actualTypeArguments.map { resolveTypeVariables(it, resolving) }.toTypedArray() + ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { resolveTypeVariables(it, resolving) }) } is WildcardType -> TypeUtils.wildcardType() - .withUpperBounds(*type.upperBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) - .withLowerBounds(*type.lowerBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray()) + .withUpperBounds(*type.upperBounds.map { resolveTypeVariables(it, resolving) }.toTypedArray()) + .withLowerBounds(*type.lowerBounds.map { resolveTypeVariables(it, resolving) }.toTypedArray()) .build() is ResolvedType -> { if (type.typeParameters.isEmpty()) { type.erasedType } else { - val actualTypeArguments = type.typeParameters.map { replaceTypeVariable(it) }.toTypedArray() + val actualTypeArguments = type.typeParameters.map { resolveTypeVariables(it) }.toTypedArray() ParameterizedTypeImpl(type.erasedType, actualTypeArguments, null) } } @@ -73,19 +76,17 @@ internal class GenericType(private val mostSpecificType: JavaType, private val o // only a variable leaked from a raw type can be bound to a type containing itself (e.g. T -> List), // erase it like the raw type does instead of expanding it forever type in resolving -> TypeUtils.getRawType(type.bounds.first(), null) ?: Any::class.java - // the most specific type binds the variables of all its supertypes - genericDeclaration is Class<*> -> generateSequence(mostSpecificType) { (it as? ParameterizedType)?.ownerType } + // the containing type binds the variables of all its supertypes + genericDeclaration is Class<*> -> generateSequence(containingType) { (it as? ParameterizedType)?.ownerType } // an inner class can also use the variables of its outer class, those are bound by its owner type (e.g. Connection.Entry) .firstNotNullOfOrNull { TypeUtils.getTypeArguments(it, genericDeclaration)?.get(type) } // keep the full type argument (e.g. List) rather than its raw class so nested generics aren't lost - ?.let { replaceTypeVariable(it, resolving + type) } + ?.let { resolveTypeVariables(it, resolving + type) } ?: type else -> type } } - else -> { - type - } + else -> type } } } diff --git a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt index 5497c8b2..762d24b0 100644 --- a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt +++ b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt @@ -3,6 +3,7 @@ package graphql.kickstart.tools import graphql.execution.DataFetcherResult import graphql.kickstart.tools.util.GraphQLLangType import graphql.kickstart.tools.util.JavaType +import graphql.kickstart.tools.util.isSubtypeOf import graphql.language.* import graphql.schema.idl.ScalarInfo import java.lang.reflect.ParameterizedType @@ -13,10 +14,6 @@ import java.util.* */ internal class TypeClassMatcher(private val definitionsByName: Map>) { - companion object { - fun isListType(realType: ParameterizedType, generic: GenericType) = generic.isTypeAssignableFromRawClass(realType, Iterable::class.java) - } - private fun error(potentialMatch: PotentialMatch, msg: String) = SchemaClassScannerError("Unable to match type definition (${potentialMatch.graphQLType}) for reference ${potentialMatch.reference} with java type (${potentialMatch.javaType}): $msg") fun match(potentialMatch: PotentialMatch): Match { @@ -27,14 +24,14 @@ internal class TypeClassMatcher(private val definitionsByName: Map when { - realType is ParameterizedType && isListType(realType, potentialMatch) -> + realType is ParameterizedType && realType.isSubtypeOf(Iterable::class.java) -> match(potentialMatch, graphQLType.type, realType.actualTypeArguments.first()) realType is Class<*> && realType.isArray -> match(potentialMatch, graphQLType.type, realType.componentType) @@ -93,8 +90,6 @@ internal class TypeClassMatcher(private val definitionsByName: Map = this as Class<*> } +internal fun ParameterizedType.isSubtypeOf(type: Class<*>): Boolean = type.isAssignableFrom(unwrap()) + /** * Replaces a parameterized type whose type arguments are all unbounded wildcards, e.g. Kotlin's Page<*>, by its raw type. */ From e1aa4f16d1b44dea02298551067d3bf3f9284f64 Mon Sep 17 00:00:00 2001 From: Oryan Date: Sat, 10 Oct 2026 16:47:34 -0400 Subject: [PATCH 3/3] Rename GenericType to GenericTypeResolver - Rename GenericType to GenericTypeResolver and unwrapGenericType to resolve - Rename the fields holding it to typeResolver Co-Authored-By: Claude Opus 5.5 --- .../{GenericType.kt => GenericTypeResolver.kt} | 6 +++--- .../kickstart/tools/SchemaClassScanner.kt | 2 +- .../kickstart/tools/TypeClassMatcher.kt | 16 ++++++++-------- .../kickstart/tools/resolver/FieldResolver.kt | 6 +++--- .../tools/resolver/MapFieldResolver.kt | 2 +- .../tools/resolver/MethodFieldResolver.kt | 18 +++++++++--------- .../tools/resolver/PropertyFieldResolver.kt | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) rename src/main/kotlin/graphql/kickstart/tools/{GenericType.kt => GenericTypeResolver.kt} (95%) diff --git a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt b/src/main/kotlin/graphql/kickstart/tools/GenericTypeResolver.kt similarity index 95% rename from src/main/kotlin/graphql/kickstart/tools/GenericType.kt rename to src/main/kotlin/graphql/kickstart/tools/GenericTypeResolver.kt index 4b442085..66a8762c 100644 --- a/src/main/kotlin/graphql/kickstart/tools/GenericType.kt +++ b/src/main/kotlin/graphql/kickstart/tools/GenericTypeResolver.kt @@ -12,7 +12,7 @@ import java.lang.reflect.WildcardType /** * @author Andrew Potter */ -internal class GenericType(private val containingType: JavaType, private val options: SchemaParserOptions) { +internal class GenericTypeResolver(private val containingType: JavaType, private val options: SchemaParserOptions) { fun getRawClass() = getRawClass(containingType) @@ -29,7 +29,7 @@ internal class GenericType(private val containingType: JavaType, private val opt * - wildcards are replaced by their upper bound, e.g. `? extends Foo` becomes `Foo` * - primitives are replaced by their boxed class, e.g. `int` becomes `Integer` */ - fun unwrapGenericType(javaType: JavaType): JavaType { + fun resolve(javaType: JavaType): JavaType { return when (val type = resolveTypeVariables(javaType)) { is ParameterizedType -> { val rawType = type.rawType @@ -42,7 +42,7 @@ internal class GenericType(private val containingType: JavaType, private val opt } val unwrapsTo = wrapper.schemaWrapper.invoke(typeArguments[wrapper.index]) - unwrapGenericType(unwrapsTo) + resolve(unwrapsTo) } is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' relative to ${TypeUtils.toString(containingType)}") is WildcardType -> type.upperBounds.firstOrNull() diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt index b3b47452..b48195fe 100644 --- a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt +++ b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt @@ -396,7 +396,7 @@ internal class SchemaClassScanner( handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType( inputValueDefinition.type, inputValueJavaType, - GenericType(javaType, options), + GenericTypeResolver(javaType, options), InputObjectReference(inputValueDefinition) ))) } else { diff --git a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt index 762d24b0..b6e4096e 100644 --- a/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt +++ b/src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt @@ -22,14 +22,14 @@ internal class TypeClassMatcher(private val definitionsByName: Map @@ -33,8 +33,8 @@ internal abstract class FieldResolver( ?: environment?.getSource() ?: throw ResolverError("Expected DataFetchingEnvironment and source object to not be null!") - if (!this.genericType.isAssignableFrom(source.javaClass)) { - throw ResolverError("Expected source object to be an instance of '${this.genericType.getRawClass().name}' but instead got '${source.javaClass.name}'") + if (!this.typeResolver.isAssignableFrom(source.javaClass)) { + throw ResolverError("Expected source object to be an instance of '${this.typeResolver.getRawClass().name}' but instead got '${source.javaClass.name}'") } source diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt index 2e135a32..70c6d41b 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt @@ -44,7 +44,7 @@ internal class MapFieldResolver( } override fun scanForMatches(): List { - return listOf(TypeClassMatcher.PotentialMatch.returnValue(field.type, mapGenericValue, genericType, SchemaClassScanner.FieldTypeReference(field.name))) + return listOf(TypeClassMatcher.PotentialMatch.returnValue(field.type, mapGenericValue, typeResolver, SchemaClassScanner.FieldTypeReference(field.name))) } override fun toString() = "MapFieldResolver{key=${field.name}}" diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt index 86889677..c65f6ed6 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt @@ -72,11 +72,11 @@ internal class MethodFieldResolver( this.field.inputValueDefinitions.forEachIndexed { index, definition -> val parameterType = this.getMethodParameterType(index) - ?.apply { genericType.getRawClass(this) } + ?.apply { typeResolver.getRawClass(this) } ?: throw ResolverError("Missing method type at position ${this.getJavaMethodParameterIndex(index)}, this is most likely a bug with graphql-java-tools") val isNonNull = definition.type is NonNullType - val isOptional = this.genericType.getRawClass(parameterType) == Optional::class.java + val isOptional = this.typeResolver.getRawClass(parameterType) == Optional::class.java args.add { environment -> val argumentPresent = environment.arguments.containsKey(definition.name) @@ -130,7 +130,7 @@ internal class MethodFieldResolver( // the class the return type unwraps to, so a value that happens to implement Future isn't mistaken for a wrapper. // Object (e.g. for a union) can't tell them apart, so then every future of a method declared to return Future is checked - val valueClass = (TypeUtils.getRawType(getUnwrappedReturnType(), null) ?: Any::class.java) + val valueClass = (TypeUtils.getRawType(getResolvedReturnType(), null) ?: Any::class.java) .takeUnless { it == Any::class.java && TypeUtils.getRawType(getReturnType(), null) == Future::class.java } return if (numberOfParameters > 0 || isSuspendFunction) { @@ -153,8 +153,8 @@ internal class MethodFieldResolver( */ private fun isConcreteScalarType(environment: DataFetchingEnvironment, type: Type<*>, genericParameterType: JavaType): Boolean { return when (type) { - is ListType -> List::class.java.isAssignableFrom(this.genericType.getRawClass(genericParameterType)) - && isConcreteScalarType(environment, type.type, this.genericType.unwrapGenericType(genericParameterType)) + is ListType -> List::class.java.isAssignableFrom(this.typeResolver.getRawClass(genericParameterType)) + && isConcreteScalarType(environment, type.type, this.typeResolver.resolve(genericParameterType)) is TypeName -> environment.graphQLSchema?.getType(type.name!!)?.let { isScalar(it) && type.name != "ID" } ?: false @@ -165,10 +165,10 @@ internal class MethodFieldResolver( } override fun scanForMatches(): List { - val returnValueMatch = TypeClassMatcher.PotentialMatch.returnValue(field.type, getUnwrappedReturnType(), genericType, SchemaClassScanner.ReturnValueReference(method)) + val returnValueMatch = TypeClassMatcher.PotentialMatch.returnValue(field.type, getResolvedReturnType(), typeResolver, SchemaClassScanner.ReturnValueReference(method)) return field.inputValueDefinitions.mapIndexed { i, inputDefinition -> - TypeClassMatcher.PotentialMatch.parameterType(inputDefinition.type, getMethodParameterType(i)!!, genericType, SchemaClassScanner.MethodParameterReference(method, i)) + TypeClassMatcher.PotentialMatch.parameterType(inputDefinition.type, getMethodParameterType(i)!!, typeResolver, SchemaClassScanner.MethodParameterReference(method, i)) } + listOf(returnValueMatch) } @@ -186,8 +186,8 @@ internal class MethodFieldResolver( method.genericReturnType } - private fun getUnwrappedReturnType(): JavaType = - getReturnType().let { genericType.unwrapGenericType(if (search.isSubscription) it.asPublisher() else it) } + private fun getResolvedReturnType(): JavaType = + getReturnType().let { typeResolver.resolve(if (search.isSubscription) it.asPublisher() else it) } private fun getIndexOffset(): Int { return if (resolverInfo is DataClassTypeResolverInfo && !method.declaringClass.isAssignableFrom(resolverInfo.dataClassType)) { diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt index 4093fe1a..b2b498ce 100644 --- a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt +++ b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt @@ -30,7 +30,7 @@ internal class PropertyFieldResolver( TypeClassMatcher.PotentialMatch.returnValue( field.type, property.genericType, - genericType, + typeResolver, SchemaClassScanner.FieldTypeReference(property.toString()) ) )