Skip to content

Commit dffbc34

Browse files
oryan-blockclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 2dbc802 commit dffbc34

8 files changed

Lines changed: 68 additions & 126 deletions

File tree

‎src/main/kotlin/graphql/kickstart/tools/GenericType.kt‎

Lines changed: 56 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import java.lang.reflect.WildcardType
1212
/**
1313
* @author Andrew Potter
1414
*/
15-
internal open class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) {
15+
internal class GenericType(private val mostSpecificType: JavaType, private val options: SchemaParserOptions) {
1616

1717
fun isTypeAssignableFromRawClass(type: ParameterizedType, clazz: Class<*>) =
1818
clazz.isAssignableFrom(getRawClass(type.rawType))
@@ -23,125 +23,69 @@ internal open class GenericType(protected val mostSpecificType: JavaType, protec
2323

2424
fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, mostSpecificType)
2525

26-
fun relativeToPotentialParent(declaringType: JavaType): RelativeTo {
27-
if (declaringType !is Class<*> || declaringType.isInterface) {
28-
return relativeToType(declaringType)
29-
}
30-
31-
val type = getGenericSuperType(mostSpecificType, declaringType)
32-
if (type == null) {
33-
error("Unable to find generic type of class ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}")
34-
} else {
35-
return relativeToType(type)
36-
}
37-
}
38-
39-
fun relativeToType(declaringType: JavaType) = RelativeTo(declaringType, mostSpecificType, options)
40-
41-
fun getGenericInterface(targetInterface: Class<*>) = getGenericInterface(mostSpecificType, targetInterface)
42-
43-
private fun getGenericInterface(type: JavaType?, targetInterface: Class<*>): JavaType? {
44-
if (type == null) {
45-
return null
46-
}
47-
48-
val raw = type as? Class<*> ?: getRawClass(type)
49-
50-
if (raw == targetInterface) {
51-
return type
52-
}
53-
54-
val possibleSubInterface = raw.genericInterfaces.find { genericInterface ->
55-
TypeUtils.isAssignable(genericInterface, targetInterface)
56-
} ?: raw.interfaces.find { iface ->
57-
TypeUtils.isAssignable(iface, targetInterface)
58-
} ?: getGenericInterface(raw.genericSuperclass, targetInterface) ?: return null
59-
60-
return getGenericInterface(possibleSubInterface, targetInterface)
61-
}
62-
63-
fun getGenericSuperType(targetSuperClass: Class<*>) = getGenericSuperType(mostSpecificType, targetSuperClass)
64-
65-
private fun getGenericSuperType(type: JavaType?, targetSuperClass: Class<*>): JavaType? {
66-
if (type == null) {
67-
return null
68-
}
69-
70-
val raw = type as? Class<*> ?: TypeUtils.getRawType(type, type)
26+
/**
27+
* Unwrap certain Java types to find the "real" class.
28+
*/
29+
fun unwrapGenericType(javaType: JavaType): JavaType {
30+
return when (val type = replaceTypeVariable(javaType)) {
31+
is ParameterizedType -> {
32+
val rawType = type.rawType
33+
val genericType = options.genericWrappers.find { it.type == rawType }
34+
?: return type
35+
36+
val typeArguments = type.actualTypeArguments
37+
if (typeArguments.size <= genericType.index) {
38+
throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!")
39+
}
7140

72-
if (raw == targetSuperClass) {
73-
return type
41+
val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index])
42+
unwrapGenericType(unwrapsTo)
43+
}
44+
is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' relative to ${TypeUtils.toString(mostSpecificType)}")
45+
is WildcardType -> type.upperBounds.firstOrNull()
46+
?: error("Unable to unwrap type, wildcard has no upper bound: $type")
47+
is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type
48+
else -> error("Unable to unwrap type: $type")
7449
}
75-
76-
return getGenericSuperType(raw.genericSuperclass, targetSuperClass)
7750
}
7851

79-
class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions) : GenericType(mostSpecificType, options) {
80-
81-
/**
82-
* Unwrap certain Java types to find the "real" class.
83-
*/
84-
fun unwrapGenericType(javaType: JavaType): JavaType {
85-
return when (val type = replaceTypeVariable(javaType)) {
86-
is ParameterizedType -> {
87-
val rawType = type.rawType
88-
val genericType = options.genericWrappers.find { it.type == rawType }
89-
?: return type
90-
91-
val typeArguments = type.actualTypeArguments
92-
if (typeArguments.size <= genericType.index) {
93-
throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!")
94-
}
95-
96-
val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index])
97-
unwrapGenericType(unwrapsTo)
98-
}
99-
is TypeVariable<*> -> error("Could not resolve type variable '${TypeUtils.toLongString(type)}' of ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}")
100-
is WildcardType -> type.upperBounds.firstOrNull()
101-
?: error("Unable to unwrap type, wildcard has no upper bound: $type")
102-
is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type
103-
else -> error("Unable to unwrap type: $type")
52+
private fun replaceTypeVariable(type: JavaType, resolving: Set<TypeVariable<*>> = emptySet()): JavaType {
53+
return when (type) {
54+
is ParameterizedType -> {
55+
val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it, resolving) }.toTypedArray()
56+
ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { replaceTypeVariable(it, resolving) })
10457
}
105-
}
106-
107-
private fun replaceTypeVariable(type: JavaType, resolving: Set<TypeVariable<*>> = emptySet()): JavaType {
108-
return when (type) {
109-
is ParameterizedType -> {
110-
val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it, resolving) }.toTypedArray()
111-
ParameterizedTypeImpl(type.rawType as Class<*>, actualTypeArguments, type.ownerType?.let { replaceTypeVariable(it, resolving) })
112-
}
113-
is WildcardType -> TypeUtils.wildcardType()
114-
.withUpperBounds(*type.upperBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray())
115-
.withLowerBounds(*type.lowerBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray())
116-
.build()
117-
is ResolvedType -> {
118-
if (type.typeParameters.isEmpty()) {
119-
type.erasedType
120-
} else {
121-
val actualTypeArguments = type.typeParameters.map { replaceTypeVariable(it) }.toTypedArray()
122-
ParameterizedTypeImpl(type.erasedType, actualTypeArguments, null)
123-
}
58+
is WildcardType -> TypeUtils.wildcardType()
59+
.withUpperBounds(*type.upperBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray())
60+
.withLowerBounds(*type.lowerBounds.map { replaceTypeVariable(it, resolving) }.toTypedArray())
61+
.build()
62+
is ResolvedType -> {
63+
if (type.typeParameters.isEmpty()) {
64+
type.erasedType
65+
} else {
66+
val actualTypeArguments = type.typeParameters.map { replaceTypeVariable(it) }.toTypedArray()
67+
ParameterizedTypeImpl(type.erasedType, actualTypeArguments, null)
12468
}
125-
is TypeVariable<*> -> {
126-
val genericDeclaration = type.genericDeclaration
127-
when {
128-
// only a variable leaked from a raw type can be bound to a type containing itself (e.g. T -> List<T>),
129-
// erase it like the raw type does instead of expanding it forever
130-
type in resolving -> TypeUtils.getRawType(type.bounds.first(), null) ?: Any::class.java
131-
// the most specific type binds the variables of all its supertypes
132-
genericDeclaration is Class<*> -> generateSequence(mostSpecificType) { (it as? ParameterizedType)?.ownerType }
133-
// an inner class can also use the variables of its outer class, those are bound by its owner type (e.g. Connection<Owner>.Entry)
134-
.firstNotNullOfOrNull { TypeUtils.getTypeArguments(it, genericDeclaration)?.get(type) }
135-
// keep the full type argument (e.g. List<Foo>) rather than its raw class so nested generics aren't lost
136-
?.let { replaceTypeVariable(it, resolving + type) }
137-
?: type
138-
else -> type
139-
}
140-
}
141-
else -> {
142-
type
69+
}
70+
is TypeVariable<*> -> {
71+
val genericDeclaration = type.genericDeclaration
72+
when {
73+
// only a variable leaked from a raw type can be bound to a type containing itself (e.g. T -> List<T>),
74+
// erase it like the raw type does instead of expanding it forever
75+
type in resolving -> TypeUtils.getRawType(type.bounds.first(), null) ?: Any::class.java
76+
// the most specific type binds the variables of all its supertypes
77+
genericDeclaration is Class<*> -> generateSequence(mostSpecificType) { (it as? ParameterizedType)?.ownerType }
78+
// an inner class can also use the variables of its outer class, those are bound by its owner type (e.g. Connection<Owner>.Entry)
79+
.firstNotNullOfOrNull { TypeUtils.getTypeArguments(it, genericDeclaration)?.get(type) }
80+
// keep the full type argument (e.g. List<Foo>) rather than its raw class so nested generics aren't lost
81+
?.let { replaceTypeVariable(it, resolving + type) }
82+
?: type
83+
else -> type
14384
}
14485
}
86+
else -> {
87+
type
88+
}
14589
}
14690
}
14791
}

‎src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ internal class SchemaClassScanner(
396396
handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType(
397397
inputValueDefinition.type,
398398
inputValueJavaType,
399-
GenericType(javaType, options).relativeToType(javaType),
399+
GenericType(javaType, options),
400400
InputObjectReference(inputValueDefinition)
401401
)))
402402
} else {

‎src/main/kotlin/graphql/kickstart/tools/TypeClassMatcher.kt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,15 @@ internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeD
109109
internal data class PotentialMatch(
110110
val graphQLType: GraphQLLangType,
111111
val javaType: JavaType,
112-
val generic: GenericType.RelativeTo,
112+
val generic: GenericType,
113113
val reference: SchemaClassScanner.Reference,
114114
val location: Location
115115
) {
116116
companion object {
117-
fun returnValue(graphQLType: GraphQLLangType, javaType: JavaType, generic: GenericType.RelativeTo, reference: SchemaClassScanner.Reference) =
117+
fun returnValue(graphQLType: GraphQLLangType, javaType: JavaType, generic: GenericType, reference: SchemaClassScanner.Reference) =
118118
PotentialMatch(graphQLType, javaType, generic, reference, Location.RETURN_TYPE)
119119

120-
fun parameterType(graphQLType: GraphQLLangType, javaType: JavaType, generic: GenericType.RelativeTo, reference: SchemaClassScanner.Reference) =
120+
fun parameterType(graphQLType: GraphQLLangType, javaType: JavaType, generic: GenericType, reference: SchemaClassScanner.Reference) =
121121
PotentialMatch(graphQLType, javaType, generic, reference, Location.PARAMETER_TYPE)
122122
}
123123
}

‎src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package graphql.kickstart.tools.resolver
22

33
import graphql.kickstart.tools.*
4-
import graphql.kickstart.tools.util.JavaType
54
import graphql.language.FieldDefinition
65
import graphql.schema.DataFetcher
76
import graphql.schema.DataFetchingEnvironment
@@ -12,11 +11,10 @@ import graphql.schema.DataFetchingEnvironment
1211
internal abstract class FieldResolver(
1312
val field: FieldDefinition,
1413
val search: FieldResolverScanner.Search,
15-
val options: SchemaParserOptions,
16-
relativeTo: JavaType
14+
val options: SchemaParserOptions
1715
) {
1816
val resolverInfo: ResolverInfo = search.resolverInfo
19-
val genericType = GenericType(search.type, options).relativeToPotentialParent(relativeTo)
17+
val genericType = GenericType(search.type, options)
2018

2119
abstract fun scanForMatches(): List<TypeClassMatcher.PotentialMatch>
2220

‎src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ internal class MapFieldResolver(
2121
field: FieldDefinition,
2222
search: FieldResolverScanner.Search,
2323
options: SchemaParserOptions,
24-
relativeTo: JavaType
25-
) : FieldResolver(field, search, options, relativeTo) {
24+
mapClass: JavaType
25+
) : FieldResolver(field, search, options) {
2626

27-
private var mapGenericValue: JavaType = getMapGenericType(relativeTo)
27+
private var mapGenericValue: JavaType = getMapGenericType(mapClass)
2828

2929
/**
3030
* Takes a type which implements Map and tries to find the

‎src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ internal class MethodFieldResolver(
4141
search: FieldResolverScanner.Search,
4242
options: SchemaParserOptions,
4343
val method: Method
44-
) : FieldResolver(field, search, options, search.type) {
44+
) : FieldResolver(field, search, options) {
4545

4646
private val log = LoggerFactory.getLogger(javaClass)
4747

‎src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import graphql.schema.DataFetcher
99
internal class MissingFieldResolver(
1010
field: FieldDefinition,
1111
options: SchemaParserOptions
12-
) : FieldResolver(field, FieldResolverScanner.Search(Any::class.java, MissingResolverInfo(), null), options, Any::class.java) {
12+
) : FieldResolver(field, FieldResolverScanner.Search(Any::class.java, MissingResolverInfo(), null), options) {
1313

1414
private val missingResolverDataFetcherProvider: MissingResolverDataFetcherProvider = options.missingResolverDataFetcherProvider
1515
?: MissingResolverDataFetcherProvider {

‎src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal class PropertyFieldResolver(
1919
search: FieldResolverScanner.Search,
2020
options: SchemaParserOptions,
2121
private val property: Field
22-
) : FieldResolver(field, search, options, property.declaringClass) {
22+
) : FieldResolver(field, search, options) {
2323

2424
override fun createDataFetcher(): DataFetcher<*> {
2525
return PropertyFieldResolverDataFetcher(createSourceResolver(), property)

0 commit comments

Comments
 (0)