Skip to content

Commit 5829339

Browse files
committed
Refactor to support JDK >= 9
1 parent ed1d245 commit 5829339

File tree

5 files changed

+282
-36
lines changed

5 files changed

+282
-36
lines changed

src/main/kotlin/com/coxautodev/graphql/tools/GenericType.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ package com.coxautodev.graphql.tools
33
import com.fasterxml.classmate.ResolvedType
44
import com.google.common.primitives.Primitives
55
import org.apache.commons.lang3.reflect.TypeUtils
6-
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
7-
import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
86
import java.lang.reflect.ParameterizedType
97
import java.lang.reflect.TypeVariable
8+
import java.lang.reflect.WildcardType
109

1110
/**
1211
* @author Andrew Potter
@@ -103,7 +102,7 @@ internal open class GenericType(protected val mostSpecificType: JavaType, protec
103102
error("Could not resolve type variable '${TypeUtils.toLongString(type)}' because declaring type is not parameterized: ${TypeUtils.toString(declaringType)}")
104103
}
105104
}
106-
is WildcardTypeImpl -> type.upperBounds.firstOrNull()
105+
is WildcardType -> type.upperBounds.firstOrNull()
107106
?: throw error("Unable to unwrap type, wildcard has no upper bound: $type")
108107
is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type
109108
else -> error("Unable to unwrap type: $type")
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/*
2+
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
3+
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4+
*
5+
*
6+
*
7+
*
8+
*
9+
*
10+
*
11+
*
12+
*
13+
*
14+
*
15+
*
16+
*
17+
*
18+
*
19+
*
20+
*
21+
*
22+
*
23+
*
24+
*/
25+
26+
package com.coxautodev.graphql.tools;
27+
28+
import java.lang.reflect.MalformedParameterizedTypeException;
29+
import java.lang.reflect.ParameterizedType;
30+
import java.lang.reflect.Type;
31+
import java.lang.reflect.TypeVariable;
32+
import java.util.Arrays;
33+
import java.util.Objects;
34+
import java.util.StringJoiner;
35+
36+
/** Implementing class for ParameterizedType interface. */
37+
38+
class ParameterizedTypeImpl implements ParameterizedType {
39+
private final Type[] actualTypeArguments;
40+
private final Class<?> rawType;
41+
private final Type ownerType;
42+
43+
private ParameterizedTypeImpl(Class<?> rawType,
44+
Type[] actualTypeArguments,
45+
Type ownerType) {
46+
this.actualTypeArguments = actualTypeArguments;
47+
this.rawType = rawType;
48+
this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass();
49+
validateConstructorArguments();
50+
}
51+
52+
private void validateConstructorArguments() {
53+
TypeVariable<?>[] formals = rawType.getTypeParameters();
54+
// check correct arity of actual type args
55+
if (formals.length != actualTypeArguments.length){
56+
throw new MalformedParameterizedTypeException();
57+
}
58+
for (int i = 0; i < actualTypeArguments.length; i++) {
59+
// check actuals against formals' bounds
60+
}
61+
}
62+
63+
/**
64+
* Static factory. Given a (generic) class, actual type arguments
65+
* and an owner type, creates a parameterized type.
66+
* This class can be instantiated with a raw type that does not
67+
* represent a generic type, provided the list of actual type
68+
* arguments is empty.
69+
* If the ownerType argument is null, the declaring class of the
70+
* raw type is used as the owner type.
71+
* <p> This method throws a MalformedParameterizedTypeException
72+
* under the following circumstances:
73+
* If the number of actual type arguments (i.e., the size of the
74+
* array {@code typeArgs}) does not correspond to the number of
75+
* formal type arguments.
76+
* If any of the actual type arguments is not an instance of the
77+
* bounds on the corresponding formal.
78+
* @param rawType the Class representing the generic type declaration being
79+
* instantiated
80+
* @param actualTypeArguments a (possibly empty) array of types
81+
* representing the actual type arguments to the parameterized type
82+
* @param ownerType the enclosing type, if known.
83+
* @return An instance of {@code ParameterizedType}
84+
* @throws MalformedParameterizedTypeException if the instantiation
85+
* is invalid
86+
*/
87+
public static ParameterizedTypeImpl make(Class<?> rawType,
88+
Type[] actualTypeArguments,
89+
Type ownerType) {
90+
return new ParameterizedTypeImpl(rawType, actualTypeArguments,
91+
ownerType);
92+
}
93+
94+
95+
/**
96+
* Returns an array of {@code Type} objects representing the actual type
97+
* arguments to this type.
98+
*
99+
* <p>Note that in some cases, the returned array be empty. This can occur
100+
* if this type represents a non-parameterized type nested within
101+
* a parameterized type.
102+
*
103+
* @return an array of {@code Type} objects representing the actual type
104+
* arguments to this type
105+
* @throws TypeNotPresentException if any of the
106+
* actual type arguments refers to a non-existent type declaration
107+
* @throws MalformedParameterizedTypeException if any of the
108+
* actual type parameters refer to a parameterized type that cannot
109+
* be instantiated for any reason
110+
* @since 1.5
111+
*/
112+
public Type[] getActualTypeArguments() {
113+
return actualTypeArguments.clone();
114+
}
115+
116+
/**
117+
* Returns the {@code Type} object representing the class or interface
118+
* that declared this type.
119+
*
120+
* @return the {@code Type} object representing the class or interface
121+
* that declared this type
122+
*/
123+
public Class<?> getRawType() {
124+
return rawType;
125+
}
126+
127+
128+
/**
129+
* Returns a {@code Type} object representing the type that this type
130+
* is a member of. For example, if this type is {@code O<T>.I<S>},
131+
* return a representation of {@code O<T>}.
132+
*
133+
* <p>If this type is a top-level type, {@code null} is returned.
134+
*
135+
* @return a {@code Type} object representing the type that
136+
* this type is a member of. If this type is a top-level type,
137+
* {@code null} is returned
138+
* @throws TypeNotPresentException if the owner type
139+
* refers to a non-existent type declaration
140+
* @throws MalformedParameterizedTypeException if the owner type
141+
* refers to a parameterized type that cannot be instantiated
142+
* for any reason
143+
*
144+
*/
145+
public Type getOwnerType() {
146+
return ownerType;
147+
}
148+
149+
/*
150+
* From the JavaDoc for java.lang.reflect.ParameterizedType
151+
* "Instances of classes that implement this interface must
152+
* implement an equals() method that equates any two instances
153+
* that share the same generic type declaration and have equal
154+
* type parameters."
155+
*/
156+
@Override
157+
public boolean equals(Object o) {
158+
if (o instanceof ParameterizedType) {
159+
// Check that information is equivalent
160+
ParameterizedType that = (ParameterizedType) o;
161+
162+
if (this == that)
163+
return true;
164+
165+
Type thatOwner = that.getOwnerType();
166+
Type thatRawType = that.getRawType();
167+
168+
if (false) { // Debugging
169+
boolean ownerEquality = (ownerType == null ?
170+
thatOwner == null :
171+
ownerType.equals(thatOwner));
172+
boolean rawEquality = (rawType == null ?
173+
thatRawType == null :
174+
rawType.equals(thatRawType));
175+
176+
boolean typeArgEquality = Arrays.equals(actualTypeArguments, // avoid clone
177+
that.getActualTypeArguments());
178+
for (Type t : actualTypeArguments) {
179+
System.out.printf("\t\t%s%s%n", t, t.getClass());
180+
}
181+
182+
System.out.printf("\towner %s\traw %s\ttypeArg %s%n",
183+
ownerEquality, rawEquality, typeArgEquality);
184+
return ownerEquality && rawEquality && typeArgEquality;
185+
}
186+
187+
return
188+
Objects.equals(ownerType, thatOwner) &&
189+
Objects.equals(rawType, thatRawType) &&
190+
Arrays.equals(actualTypeArguments, // avoid clone
191+
that.getActualTypeArguments());
192+
} else
193+
return false;
194+
}
195+
196+
@Override
197+
public int hashCode() {
198+
return
199+
Arrays.hashCode(actualTypeArguments) ^
200+
Objects.hashCode(ownerType) ^
201+
Objects.hashCode(rawType);
202+
}
203+
204+
public String toString() {
205+
StringBuilder sb = new StringBuilder();
206+
207+
if (ownerType != null) {
208+
sb.append(ownerType.getTypeName());
209+
210+
sb.append("$");
211+
212+
if (ownerType instanceof ParameterizedTypeImpl) {
213+
// Find simple name of nested type by removing the
214+
// shared prefix with owner.
215+
sb.append(rawType.getName().replace( ((ParameterizedTypeImpl)ownerType).rawType.getName() + "$",
216+
""));
217+
} else
218+
sb.append(rawType.getSimpleName());
219+
} else
220+
sb.append(rawType.getName());
221+
222+
if (actualTypeArguments != null) {
223+
StringJoiner sj = new StringJoiner(", ", "<", ">");
224+
sj.setEmptyValue("");
225+
for(Type t: actualTypeArguments) {
226+
sj.add(t.getTypeName());
227+
}
228+
sb.append(sj.toString());
229+
}
230+
231+
return sb.toString();
232+
}
233+
}

src/main/kotlin/com/coxautodev/graphql/tools/SchemaParserBuilder.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import kotlinx.coroutines.reactive.publish
2020
import org.antlr.v4.runtime.RecognitionException
2121
import org.antlr.v4.runtime.misc.ParseCancellationException
2222
import org.reactivestreams.Publisher
23-
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
2423
import java.util.concurrent.CompletableFuture
2524
import java.util.concurrent.CompletionStage
2625
import java.util.concurrent.Future

src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import graphql.language.NonNullType
66
import graphql.language.ObjectTypeDefinition
77
import graphql.language.ObjectTypeExtensionDefinition
88
import graphql.language.Type
9-
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
9+
import java.lang.reflect.ParameterizedType
1010

1111
/**
1212
* @author Andrew Potter
@@ -28,8 +28,8 @@ internal fun ObjectTypeDefinition.getExtendedFieldDefinitions(extensions: List<O
2828
}
2929

3030
internal fun JavaType.unwrap(): Class<out Any> =
31-
if (this is ParameterizedTypeImpl) {
32-
this.rawType
31+
if (this is ParameterizedType) {
32+
this.rawType as Class<*>
3333
} else {
3434
this as Class<*>
3535
}

src/test/kotlin/com/coxautodev/graphql/tools/MethodFieldResolverDataFetcherTest.kt

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,38 +25,36 @@ class MethodFieldResolverDataFetcherTest {
2525
@Test
2626
fun `data fetcher executes suspend function on coroutineContext defined by options`() {
2727
// setup
28-
val dispatcher = Dispatchers.IO
29-
val job = Job()
30-
val options = SchemaParserOptions.Builder()
31-
.coroutineContext(dispatcher + job)
32-
.build()
28+
val suspendClass = SuspendClass()
3329

34-
val resolver = createFetcher("active", object : GraphQLResolver<DataClass> {
35-
suspend fun isActive(data: DataClass): Boolean {
36-
return coroutineContext[dispatcher.key] == dispatcher &&
37-
coroutineContext[Job] == job.children.first()
38-
}
39-
}, options = options)
30+
val resolver = createFetcher("active", suspendClass, options = suspendClass.options)
4031

4132
// expect
4233
@Suppress("UNCHECKED_CAST")
4334
val future = resolver.get(createEnvironment(DataClass())) as CompletableFuture<Boolean>
4435
Assert.assertTrue(future.get())
4536
}
4637

38+
class SuspendClass : GraphQLResolver<DataClass> {
39+
val dispatcher = Dispatchers.IO
40+
val job = Job()
41+
val options = SchemaParserOptions.Builder()
42+
.coroutineContext(dispatcher + job)
43+
.build()
44+
45+
suspend fun isActive(data: DataClass): Boolean {
46+
return coroutineContext[dispatcher.key] == dispatcher &&
47+
coroutineContext[Job] == job.children.first()
48+
}
49+
}
50+
4751
@ExperimentalCoroutinesApi
4852
@Test
4953
fun `canceling subscription Publisher also cancels underlying Kotlin coroutine channel`() {
5054
// setup
51-
val channel = Channel<String>(10)
52-
channel.offer("A")
53-
channel.offer("B")
55+
val doubleChannel = DoubleChannel()
5456

55-
val resolver = createFetcher("onDataNameChanged", object : GraphQLResolver<DataClass> {
56-
fun onDataNameChanged(date: DataClass): ReceiveChannel<String> {
57-
return channel
58-
}
59-
})
57+
val resolver = createFetcher("onDataNameChanged", doubleChannel)
6058

6159
// expect
6260
@Suppress("UNCHECKED_CAST")
@@ -67,21 +65,25 @@ class MethodFieldResolverDataFetcherTest {
6765

6866
subscriber.cancel()
6967
Thread.sleep(100)
70-
Assert.assertTrue(channel.isClosedForReceive)
68+
Assert.assertTrue(doubleChannel.channel.isClosedForReceive)
69+
}
70+
71+
class DoubleChannel : GraphQLResolver<DataClass> {
72+
val channel = Channel<String>(10)
73+
init {
74+
channel.offer("A")
75+
channel.offer("B")
76+
}
77+
78+
fun onDataNameChanged(date: DataClass): ReceiveChannel<String> {
79+
return channel
80+
}
7181
}
7282

7383
@Test
7484
fun `canceling underlying Kotlin coroutine channel also cancels subscription Publisher`() {
7585
// setup
76-
val channel = Channel<String>(10)
77-
channel.offer("A")
78-
channel.close(IllegalStateException("Channel error"))
79-
80-
val resolver = createFetcher("onDataNameChanged", object : GraphQLResolver<DataClass> {
81-
fun onDataNameChanged(date: DataClass): ReceiveChannel<String> {
82-
return channel
83-
}
84-
})
86+
val resolver = createFetcher("onDataNameChanged", OnDataNameChanged())
8587

8688
// expect
8789
@Suppress("UNCHECKED_CAST")
@@ -92,6 +94,19 @@ class MethodFieldResolverDataFetcherTest {
9294
subscriber.expectErrorWithMessage(IllegalStateException::class.java, "Channel error")
9395
}
9496

97+
class OnDataNameChanged : GraphQLResolver<DataClass> {
98+
val channel = Channel<String>(10)
99+
init {
100+
channel.offer("A")
101+
channel.close(IllegalStateException("Channel error"))
102+
}
103+
104+
fun onDataNameChanged(date: DataClass): ReceiveChannel<String> {
105+
return channel
106+
}
107+
}
108+
109+
95110
private fun createFetcher(
96111
methodName: String,
97112
resolver: GraphQLResolver<*>,

0 commit comments

Comments
 (0)