Skip to content

Commit

Permalink
Merge pull request #385 from velias/281-generics
Browse files Browse the repository at this point in the history
#281 - support for generics
  • Loading branch information
phillip-kruger authored Sep 1, 2020
2 parents 475845b + 2d1ade7 commit 7020d92
Show file tree
Hide file tree
Showing 25 changed files with 571 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -72,21 +73,8 @@ public static boolean isEnum(ClassInfo classInfo) {
* @return true if it is
*/
public static boolean isNumberLikeTypeOrCollectionThereOf(Type type) {
return isTypeOrCollectionThereOf(type,
BYTE,
BYTE_PRIMATIVE,
SHORT,
SHORT_PRIMATIVE,
INTEGER,
INTEGER_PRIMATIVE,
BIG_INTEGER,
DOUBLE,
DOUBLE_PRIMATIVE,
BIG_DECIMAL,
LONG,
LONG_PRIMATIVE,
FLOAT,
FLOAT_PRIMATIVE);
return isTypeOrCollectionThereOf(type, BYTE, BYTE_PRIMATIVE, SHORT, SHORT_PRIMATIVE, INTEGER, INTEGER_PRIMATIVE,
BIG_INTEGER, DOUBLE, DOUBLE_PRIMATIVE, BIG_DECIMAL, LONG, LONG_PRIMATIVE, FLOAT, FLOAT_PRIMATIVE);
}

/**
Expand All @@ -96,17 +84,8 @@ public static boolean isNumberLikeTypeOrCollectionThereOf(Type type) {
* @return true if it is
*/
public static boolean isDateLikeTypeOrCollectionThereOf(Type type) {
return isTypeOrCollectionThereOf(type,
LOCALDATE,
LOCALTIME,
LOCALDATETIME,
ZONEDDATETIME,
OFFSETDATETIME,
OFFSETTIME,
UTIL_DATE,
SQL_DATE,
SQL_TIMESTAMP,
SQL_TIME);
return isTypeOrCollectionThereOf(type, LOCALDATE, LOCALTIME, LOCALDATETIME, ZONEDDATETIME, OFFSETDATETIME, OFFSETTIME,
UTIL_DATE, SQL_DATE, SQL_TIMESTAMP, SQL_TIME);
}

private static boolean isTypeOrCollectionThereOf(Type type, DotName... valid) {
Expand All @@ -133,6 +112,68 @@ public static boolean isAsyncType(Type type) {
return type.name().equals(COMPLETABLE_FUTURE) || type.name().equals(COMPLETION_STAGE);
}

/**
* Return true if type is java array, or it is Collection type which is handled as GraphQL array
*
* @param type to check
* @return
* @see #isCollection(Type)
*/
public static boolean isCollectionOrArray(Type type) {
return type.kind().equals(Type.Kind.ARRAY) || isCollection(type);
}

/**
* Return true if type is java Collection type which is handled as GraphQL array
*
* @param type to check
* @return
* @see #isCollectionOrArray(Type)
*/
public static boolean isCollection(Type type) {
if (type.kind().equals(Type.Kind.PARAMETERIZED_TYPE)) {

ClassInfo clazz = ScanningContext.getIndex().getClassByName(type.name());
if (clazz == null) {
// use classloader instead of jandex to handle basic java classes/interfaces
try {
Class<?> clazzLoaded = Classes.class.getClassLoader().loadClass(type.name().toString());
return Collection.class.isAssignableFrom(clazzLoaded);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Info not found in Jandex index nor classpath for class name:" + type.name());
}
}
if (clazz.name().equals(COLLECTION)) {
return true;
}

// we have to go recursively over all super-interfaces as Jandex provides only direct interfaces
// implemented in the class itself
for (Type intf : clazz.interfaceTypes()) {
if (isCollection(intf))
return true;
}
}
return false;
}

/**
* Return true if given type is parametrized type unwrapped/handled by the runtime before the serialization
* (Optional<>, CompletableFutur<>, CompletionStage<> etc)
*
* @param type to be checked
* @return true if type is unwrapped by the runtime
* @see #isOptional(Type)
* @see #isAsyncType(Type)
*/
public static boolean isUnwrappedType(Type type) {
return type.kind().equals(Type.Kind.PARAMETERIZED_TYPE) && (isOptional(type) // Optional<>
|| isAsyncType(type)) // CompletableFutur or CompletionStage
;
}

public static final DotName COLLECTION = DotName.createSimple(Collection.class.getName());

public static final DotName ENUM = DotName.createSimple(Enum.class.getName());
public static final DotName OPTIONAL = DotName.createSimple(Optional.class.getName());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private <T> void createAndAddToSchema(ReferenceType referenceType, Creator creat
while (!referenceCreator.values(referenceType).isEmpty()) {
Reference reference = referenceCreator.values(referenceType).poll();
ClassInfo classInfo = ScanningContext.getIndex().getClassByName(DotName.createSimple(reference.getClassName()));
consumer.accept((T) creator.create(classInfo));
consumer.accept((T) creator.create(classInfo, reference));
}
}

Expand All @@ -190,7 +190,7 @@ private <T> boolean findOutstandingAndAddToSchema(ReferenceType referenceType, C
Reference reference = referenceCreator.values(referenceType).poll();
ClassInfo classInfo = ScanningContext.getIndex().getClassByName(DotName.createSimple(reference.getClassName()));
if (!contains.test(reference.getName())) {
consumer.accept((T) creator.create(classInfo));
consumer.accept((T) creator.create(classInfo, reference));
allDone = false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static Optional<Array> createArray(Type fieldType, Type methodType) {
}

public static Optional<Array> createArray(Type fieldType, Type methodType, boolean batched) {
if (isCollectionOrArray(methodType) && !batched) {
if (Classes.isCollectionOrArray(methodType) && !batched) {
Array.Type arrayType = getModelType(methodType);
int depth = getParameterizedDepth(methodType);
Array array = new Array(methodType.name().toString(), arrayType, depth);
Expand All @@ -56,28 +56,13 @@ public static Optional<Array> createArray(Type fieldType, Type methodType, boole
array.setNotEmpty(true);
}
return Optional.of(array);
} else if (isParameterizedType(methodType) || batched) {
} else if (Classes.isUnwrappedType(methodType) || batched) {
Type nestedType = methodType.asParameterizedType().arguments().get(0);
return createArray(nestedType);
}
return Optional.empty();
}

private static boolean isCollectionOrArray(Type type) {
return type.kind().equals(Type.Kind.ARRAY) || isCollection(type);
}

private static boolean isCollection(Type type) {
return type.kind().equals(Type.Kind.PARAMETERIZED_TYPE) // Maybe Collection
&& !Classes.isOptional(type) // Not a Optional<>
&& !Classes.isAsyncType(type) // Not CompletableFutur or CompletionStage
;
}

private static boolean isParameterizedType(Type type) {
return type.kind().equals(Type.Kind.PARAMETERIZED_TYPE);
}

private static Array.Type getModelType(Type type) {
if (type.kind().equals(Type.Kind.ARRAY)) {
return Array.Type.ARRAY;
Expand All @@ -94,11 +79,11 @@ private static int getParameterizedDepth(Type type, int depth) {
depth = depth + 1;
Type typeInArray = type.asArrayType().component();
return getParameterizedDepth(typeInArray, depth);
} else if (isCollection(type)) {
} else if (Classes.isCollection(type)) {
depth = depth + 1;
Type typeInCollection = type.asParameterizedType().arguments().get(0);
return getParameterizedDepth(typeInCollection, depth);
} else if (type.kind().equals(Type.Kind.PARAMETERIZED_TYPE)) {
} else if (Classes.isUnwrappedType(type)) {
Type nestedType = type.asParameterizedType().arguments().get(0);
return getParameterizedDepth(nestedType, depth);
}
Expand All @@ -108,7 +93,7 @@ private static int getParameterizedDepth(Type type, int depth) {
private static boolean markParameterizedTypeNonNull(Type fieldType, Type methodType) {
if (fieldType == null)
fieldType = methodType;
if (isCollectionOrArray(fieldType)) {
if (Classes.isCollectionOrArray(fieldType)) {
Type typeInCollection = getTypeInCollection(fieldType);
Type methodTypeInCollection = getTypeInCollection(methodType);
Annotations annotationsInParameterizedType = Annotations.getAnnotationsForArray(typeInCollection,
Expand All @@ -120,11 +105,11 @@ private static boolean markParameterizedTypeNonNull(Type fieldType, Type methodT
}

private static Type getTypeInCollection(Type type) {
if (isCollectionOrArray(type)) {
if (Classes.isCollectionOrArray(type)) {
if (type.kind().equals(Type.Kind.ARRAY)) {
Type typeInArray = type.asArrayType().component();
return getTypeInCollection(typeInArray);
} else if (type.kind().equals(Type.Kind.PARAMETERIZED_TYPE)) {
} else {
Type typeInCollection = type.asParameterizedType().arguments().get(0);
return getTypeInCollection(typeInCollection);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public FieldCreator(ReferenceCreator referenceCreator) {
* @param methodInfo the java method
* @return a Field model object
*/
public Optional<Field> createFieldForInterface(MethodInfo methodInfo) {
public Optional<Field> createFieldForInterface(MethodInfo methodInfo, Reference parentObjectReference) {
Annotations annotationsForMethod = Annotations.getAnnotationsForInterfaceField(methodInfo);

if (!IgnoreHelper.shouldIgnore(annotationsForMethod)) {
Expand All @@ -54,7 +54,8 @@ public Optional<Field> createFieldForInterface(MethodInfo methodInfo) {

// Field Type
validateFieldType(Direction.OUT, methodInfo);
Reference reference = referenceCreator.createReferenceForInterfaceField(returnType, annotationsForMethod);
Reference reference = referenceCreator.createReferenceForInterfaceField(returnType, annotationsForMethod,
parentObjectReference);

Field field = new Field(methodInfo.name(),
MethodHelper.getPropertyName(Direction.OUT, methodInfo.name()),
Expand Down Expand Up @@ -93,7 +94,8 @@ public Optional<Field> createFieldForInterface(MethodInfo methodInfo) {
* @param methodInfo the java method
* @return a Field model object
*/
public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldInfo, MethodInfo methodInfo) {
public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldInfo, MethodInfo methodInfo,
Reference parentObjectReference) {
Annotations annotationsForPojo = Annotations.getAnnotationsForPojo(direction, fieldInfo, methodInfo);

if (!IgnoreHelper.shouldIgnore(annotationsForPojo, fieldInfo)) {
Expand All @@ -110,7 +112,7 @@ public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldIn
Type fieldType = getFieldType(fieldInfo, methodType);

Reference reference = referenceCreator.createReferenceForPojoField(direction, fieldType, methodType,
annotationsForPojo);
annotationsForPojo, parentObjectReference);

Field field = new Field(methodInfo.name(),
MethodHelper.getPropertyName(direction, methodInfo.name()),
Expand Down Expand Up @@ -148,7 +150,7 @@ public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldIn
* @param fieldInfo the java property
* @return a Field model object
*/
public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldInfo) {
public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldInfo, Reference parentObjectReference) {
if (Modifier.isPublic(fieldInfo.flags())) {
Annotations annotationsForPojo = Annotations.getAnnotationsForPojo(direction, fieldInfo);

Expand All @@ -164,7 +166,7 @@ public Optional<Field> createFieldForPojo(Direction direction, FieldInfo fieldIn
Optional<String> maybeDescription = DescriptionHelper.getDescriptionForField(annotationsForPojo, fieldType);

Reference reference = referenceCreator.createReferenceForPojoField(direction, fieldType, fieldType,
annotationsForPojo);
annotationsForPojo, parentObjectReference);

Field field = new Field(fieldInfo.name(),
MethodHelper.getPropertyName(direction, fieldInfo.name()),
Expand Down
Loading

0 comments on commit 7020d92

Please sign in to comment.