package io.vertx.codegen.type;
import io.vertx.codegen.Helper;
import io.vertx.codegen.MapperKind;
import io.vertx.codegen.ModuleInfo;
import io.vertx.codegen.TypeParamInfo;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.annotations.ModuleGen;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TypeReflectionFactory {
public static TypeInfo create(Type type) {
if (type == void.class) {
return VoidTypeInfo.INSTANCE;
} else if (type instanceof Class) {
String fqcn = type.getTypeName();
Class<?> classType = (Class<?>) type;
if (classType.isPrimitive()) {
return PrimitiveTypeInfo.PRIMITIVES.get(classType.getName());
} else {
Package pkg = classType.getPackage();
ModuleInfo module = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classType.getClassLoader());
try {
while (pkg != null) {
ModuleGen annotation = pkg.getAnnotation(ModuleGen.class);
if (annotation != null) {
module = new ModuleInfo(pkg.getName(), annotation.name(), annotation.groupPackage());
break;
} else {
int pos = pkg.getName().lastIndexOf('.');
if (pos == -1) {
break;
} else {
pkg = Package.getPackage(pkg.getName().substring(0, pos));
}
}
}
} finally {
Thread.currentThread().setContextClassLoader(loader);
}
if (classType.isEnum()) {
return new EnumTypeInfo(
fqcn,
classType.getDeclaredAnnotation(VertxGen.class) != null,
Stream.of(classType.getEnumConstants()).map(Object::toString).collect(Collectors.toList()),
module,
false
);
} else {
ClassKind kind = ClassKind.getKind(fqcn, classType.getAnnotation(VertxGen.class) != null);
List<TypeParamInfo.Class> typeParams = new ArrayList<>();
int index = 0;
for (java.lang.reflect.TypeVariable<? extends Class<?>> var : classType.getTypeParameters()) {
typeParams.add(new TypeParamInfo.Class(classType.getName(), index++, var.getName()));
}
if (kind == ClassKind.API) {
java.lang.reflect.TypeVariable<Class<Handler>> classTypeVariable = Handler.class.getTypeParameters()[0];
Type handlerArg = Helper.resolveTypeParameter(type, classTypeVariable);
return new ApiTypeInfo(fqcn, true, typeParams, handlerArg != null ? create(handlerArg) : null, module, false, false, null);
} else {
DataObjectInfo dataObject;
if (classType.getDeclaredAnnotation(DataObject.class) != null) {
MapperInfo serializer = getDataObjectSerializer(classType);
MapperInfo deserializer = getDataObjectDeserializer(classType);
dataObject = new DataObjectInfo(true, serializer, deserializer);
} else {
dataObject = null;
}
return new ClassTypeInfo(kind, fqcn, module, false, typeParams, dataObject);
}
}
}
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
List<TypeInfo> args = Arrays.asList(parameterizedType.getActualTypeArguments()).
stream().
map(TypeReflectionFactory::create).
collect(Collectors.toList());
Type raw = parameterizedType.getRawType();
return new ParameterizedTypeInfo((ClassTypeInfo) create(raw), false, args);
} else if (type instanceof java.lang.reflect.TypeVariable) {
java.lang.reflect.TypeVariable typeVar = (java.lang.reflect.TypeVariable) type;
TypeParamInfo param = TypeParamInfo.create(typeVar);
return new TypeVariableInfo(param, false, ((java.lang.reflect.TypeVariable) type).getName());
} else {
throw new IllegalArgumentException("Unsupported type " + type);
}
}
private static MapperInfo getDataObjectSerializer(Class<?> type) {
try {
Method m = type.getMethod("toJson");
if (Modifier.isPublic(m.getModifiers()) && m.getReturnType().equals(JsonObject.class)) {
MapperInfo serializer = new MapperInfo();
serializer.setQualifiedName(type.getName());
serializer.setKind(MapperKind.SELF);
return serializer;
}
} catch (NoSuchMethodException ignore) {
}
return null;
}
private static MapperInfo getDataObjectDeserializer(Class<?> type) {
try {
if (
!Modifier.isAbstract(type.getModifiers()) &&
!type.isInterface() &&
Modifier.isPublic(type.getConstructor(JsonObject.class).getModifiers())
) {
MapperInfo deserializer = new MapperInfo();
deserializer.setQualifiedName(type.getName());
deserializer.setKind(MapperKind.SELF);
return deserializer;
}
} catch (NoSuchMethodException ignore) {
}
return null;
}
}