package io.vertx.test.codegen;
import io.vertx.codegen.type.ClassKind;
import io.vertx.codegen.Helper;
import io.vertx.codegen.type.*;
import io.vertx.codegen.TypeParamInfo;
import io.vertx.codegen.testmodel.TestDataObject;
import io.vertx.codegen.type.VoidTypeInfo;
import io.vertx.core.AsyncResult;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.test.codegen.testapi.GenericInterface;
import io.vertx.test.codegen.testapi.streams.InterfaceExtentingReadStream;
import io.vertx.test.codegen.testapi.streams.InterfaceSubtypingReadStream;
import io.vertx.test.codegen.testapi.streams.ReadStreamWithParameterizedTypeArg;
import io.vertx.test.codegen.testtype.ApiHolder;
import io.vertx.test.codegen.testtype.ApiObject;
import io.vertx.test.codegen.testtype.AsyncHolder;
import io.vertx.test.codegen.testtype.BasicHolder;
import io.vertx.test.codegen.testtype.CollectionHolder;
import io.vertx.test.codegen.testtype.DataObjectHolder;
import io.vertx.test.codegen.testtype.EnumHolder;
import io.vertx.test.codegen.testtype.HandlerHolder;
import io.vertx.test.codegen.testtype.JsonHolder;
import io.vertx.test.codegen.testtype.OtherHolder;
import io.vertx.test.codegen.testtype.StreamHolder;
import io.vertx.test.codegen.testtype.ThrowableHolder;
import io.vertx.test.codegen.testtype.TypeParamHolder;
import io.vertx.test.codegen.testtype.VoidHolder;
import org.junit.Test;
import javax.lang.model.element.TypeElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.*;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class TypeInfoTest {
private void doTest(Class<?> container, Consumer<Map<String, TypeInfo>> assertion) throws Exception {
Map<String, TypeInfo> reflectedMap = Stream.of(container.getDeclaredMethods()).filter(m -> Modifier.isPublic(m.getModifiers())).
collect(Collectors.toMap(Method::getName, m -> TypeReflectionFactory.create(m.getGenericReturnType())));
assertion.accept(reflectedMap);
Utils.assertProcess((proc, env) -> {
TypeMirrorFactory factory = new TypeMirrorFactory(proc.getElementUtils(), proc.getTypeUtils());
TypeElement modelMap = proc.getElementUtils().getTypeElement(container.getName());
Map<String, TypeInfo> collect = modelMap.getEnclosedElements().stream().
flatMap(Helper.FILTER_METHOD).
filter(elt -> elt.getModifiers().contains(javax.lang.model.element.Modifier.PUBLIC)).
collect(Collectors.toMap(m -> m.getSimpleName().toString(), m -> factory.create(m.getReturnType())));
assertion.accept(collect);
});
}
@Test
public void testVoid() throws Exception {
doTest(VoidHolder.class, map -> {
VoidTypeInfo voidType = (VoidTypeInfo) map.get("voidType");
assertEquals("void", voidType.getName());
assertEquals("void", voidType.getSimpleName());
assertEquals(ClassKind.OTHER, voidType.getKind());
assertClass(map.get("VoidType"), "java.lang.Void", ClassKind.VOID);
});
}
@Test
public void testApi() throws Exception {
doTest(ApiHolder.class, map -> {
ApiTypeInfo api = assertApi(map.get("api"), ApiObject.class.getName());
assertEquals(ApiObject.class.getName(), api.getName());
ParameterizedTypeInfo apiParameterizedByClass = assertParameterized(map.get("apiParameterizedByClass"), GenericInterface.class.getName() + "<java.lang.String>", ClassKind.API);
assertClass(apiParameterizedByClass.getArg(0), "java.lang.String", ClassKind.STRING);
ParameterizedTypeInfo apiParameterizedByClassTypeParam = assertParameterized(map.get("apiParameterizedByClassTypeParam"), GenericInterface.class.getName() + "<ClassTypeParam>", ClassKind.API);
TypeParamInfo.Class classTypeParam = (TypeParamInfo.Class) assertTypeVariable(apiParameterizedByClassTypeParam.getArg(0), "ClassTypeParam").getParam();
assertEquals("ClassTypeParam", classTypeParam.getName());
ParameterizedTypeInfo apiParameterizedByMethodTypeParam = assertParameterized(map.get("apiParameterizedByMethodTypeParam"), GenericInterface.class.getName() + "<MethodTypeParam>", ClassKind.API);
TypeParamInfo.Method methodTypeParam = (TypeParamInfo.Method) assertTypeVariable(apiParameterizedByMethodTypeParam.getArg(0), "MethodTypeParam").getParam();
assertEquals("MethodTypeParam", methodTypeParam.getName());
});
}
@Test
public void testTypeParam() throws Exception {
doTest(TypeParamHolder.class, map -> {
TypeParamInfo.Class classTypeParam = (TypeParamInfo.Class) assertTypeVariable(map.get("classTypeParam"), "ClassTypeParam").getParam();
assertEquals("ClassTypeParam", classTypeParam.getName());
TypeParamInfo.Method methodTypeParam = (TypeParamInfo.Method) assertTypeVariable(map.get("methodTypeParam"), "MethodTypeParam").getParam();
assertEquals("MethodTypeParam", methodTypeParam.getName());
});
}
@Test
public void testClass() throws Exception {
doTest(OtherHolder.class, map -> {
assertClass(map.get("classType"), "java.util.Locale", ClassKind.OTHER);
assertClass(map.get("interfaceType"), "java.lang.Runnable", ClassKind.OTHER);
assertClass(map.get("genericInterface"), "java.util.concurrent.Callable", ClassKind.OTHER);
assertClass(assertParameterized(map.get("interfaceParameterizedByInterface"), "java.util.concurrent.Callable<java.lang.Runnable>", ClassKind.OTHER).getArg(0), "java.lang.Runnable", ClassKind.OTHER);
assertClass(assertParameterized(map.get("interfaceParameterizedByClass"), "java.util.concurrent.Callable<java.util.Locale>", ClassKind.OTHER).getArg(0), "java.util.Locale", ClassKind.OTHER);
assertTypeVariable(assertParameterized(map.get("interfaceParameterizedByClassTypeParam"), "java.util.concurrent.Callable<ClassTypeParam>", ClassKind.OTHER).getArg(0), "ClassTypeParam");
assertTypeVariable(assertParameterized(map.get("interfaceParameterizedByMethodTypeParam"), "java.util.concurrent.Callable<MethodTypeParam>", ClassKind.OTHER).getArg(0), "MethodTypeParam");
assertClass(assertParameterized(map.get("classParameterizedByInterface"), "java.util.Vector<java.lang.Runnable>", ClassKind.OTHER).getArg(0), "java.lang.Runnable", ClassKind.OTHER);
assertClass(assertParameterized(map.get("classParameterizedByClass"), "java.util.Vector<java.util.Locale>", ClassKind.OTHER).getArg(0), "java.util.Locale", ClassKind.OTHER);
assertTypeVariable(assertParameterized(map.get("classParameterizedByClassTypeParam"), "java.util.Vector<ClassTypeParam>", ClassKind.OTHER).getArg(0), "ClassTypeParam");
assertTypeVariable(assertParameterized(map.get("classParameterizedByMethodTypeParam"), "java.util.Vector<MethodTypeParam>", ClassKind.OTHER).getArg(0), "MethodTypeParam");
});
}
@Test
public void testHandler() throws Exception {
doTest(HandlerHolder.class, map -> {
assertClass(assertHandler(map.get("handlerVoid"), "java.lang.Void"), "java.lang.Void", ClassKind.VOID);
assertClass(assertHandler(map.get("handlerString"), "java.lang.String"), "java.lang.String", ClassKind.STRING);
assertClass(assertParameterized(assertHandler(map.get("handlerListString"), "java.util.List<java.lang.String>"), "java.util.List<java.lang.String>", ClassKind.LIST).getArg(0), "java.lang.String", ClassKind.STRING);
assertApi(assertParameterized(assertHandler(map.get("handlerListApi"), "java.util.List<" + ApiObject.class.getName() + ">"), "java.util.List<" + ApiObject.class.getName() + ">", ClassKind.LIST).getArg(0), ApiObject.class.getName());
assertTypeVariable(assertParameterized(assertHandler(map.get("handlerParameterizedByClassTypeParam"), GenericInterface.class.getName() + "<ClassTypeParam>"), GenericInterface.class.getName() + "<ClassTypeParam>", ClassKind.API).getArg(0), "ClassTypeParam");
assertTypeVariable(assertParameterized(assertHandler(map.get("handlerParameterizedByMethodTypeParam"), GenericInterface.class.getName() + "<MethodTypeParam>"), GenericInterface.class.getName() + "<MethodTypeParam>", ClassKind.API).getArg(0), "MethodTypeParam");
});
}
@Test
public void testAsync() throws Exception {
doTest(AsyncHolder.class, map -> {
assertClass(assertAsync(map.get("asyncVoid"), "java.lang.Void"), "java.lang.Void", ClassKind.VOID);
assertClass(assertAsync(map.get("asyncString"), "java.lang.String"), "java.lang.String", ClassKind.STRING);
assertClass(assertParameterized(assertAsync(map.get("asyncListString"), "java.util.List<java.lang.String>"), "java.util.List<java.lang.String>", ClassKind.LIST).getArg(0), "java.lang.String", ClassKind.STRING);
assertApi(assertParameterized(assertAsync(map.get("asyncListApi"), "java.util.List<" + ApiObject.class.getName() + ">"), "java.util.List<" + ApiObject.class.getName() + ">", ClassKind.LIST).getArg(0), ApiObject.class.getName());
assertTypeVariable(assertParameterized(assertAsync(map.get("asyncParameterizedByClassTypeParam"), GenericInterface.class.getName() + "<ClassTypeParam>"), GenericInterface.class.getName() + "<ClassTypeParam>", ClassKind.API).getArg(0), "ClassTypeParam");
assertTypeVariable(assertParameterized(assertAsync(map.get("asyncParameterizedByMethodTypeParam"), GenericInterface.class.getName() + "<MethodTypeParam>"), GenericInterface.class.getName() + "<MethodTypeParam>", ClassKind.API).getArg(0), "MethodTypeParam");
});
}
@Test
public void testDataObject() throws Exception {
doTest(DataObjectHolder.class, map -> {
assertDataObject(map.get("dataObject"), TestDataObject.class.getName(), ClassKind.DATA_OBJECT);
});
}
@Test
public void testJson() throws Exception {
doTest(JsonHolder.class, map -> {
assertClass(map.get("jsonObject"), JsonObject.class.getName(), ClassKind.JSON_OBJECT);
assertClass(map.get("jsonArray"), JsonArray.class.getName(), ClassKind.JSON_ARRAY);
});
}
@Test
public void testThrowable() throws Exception {
doTest(ThrowableHolder.class, map -> {
assertClass(map.get("throwable"), "java.lang.Throwable", ClassKind.THROWABLE);
});
}
@Test
public void testBasic() throws Exception {
doTest(BasicHolder.class, map -> {
assertPrimitive(map.get("booleanType"), "boolean");
assertPrimitive(map.get("byteType"), "byte");
assertPrimitive(map.get("shortType"), "short");
assertPrimitive(map.get("intType"), "int");
assertPrimitive(map.get("longType"), "long");
assertPrimitive(map.get("floatType"), "float");
assertPrimitive(map.get("doubleType"), "double");
assertPrimitive(map.get("charType"), "char");
assertClass(map.get("BooleanType"), "java.lang.Boolean", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("ShortType"), "java.lang.Short", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("IntegerType"), "java.lang.Integer", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("LongType"), "java.lang.Long", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("FloatType"), "java.lang.Float", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("DoubleType"), "java.lang.Double", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("CharacterType"), "java.lang.Character", ClassKind.BOXED_PRIMITIVE);
assertClass(map.get("StringType"), "java.lang.String", ClassKind.STRING);
});
}
private PrimitiveTypeInfo assertPrimitive(TypeInfo type, String expectedName) {
assertEquals(PrimitiveTypeInfo.class, type.getClass());
PrimitiveTypeInfo primitiveType = (PrimitiveTypeInfo) type;
assertEquals(ClassKind.PRIMITIVE, primitiveType.getKind());
assertEquals(expectedName, primitiveType.getName());
return primitiveType;
}
private TypeVariableInfo assertTypeVariable(TypeInfo type, String expectedName) {
assertEquals(TypeVariableInfo.class, type.getClass());
TypeVariableInfo classType = (TypeVariableInfo) type;
assertEquals(ClassKind.OBJECT, classType.getKind());
assertEquals(expectedName, classType.getName());
return classType;
}
private ClassTypeInfo assertClass(TypeInfo type, String expectedName, ClassKind expectedKind) {
assertEquals(ClassTypeInfo.class, type.getClass());
ClassTypeInfo classType = (ClassTypeInfo) type;
assertEquals(expectedKind, classType.getKind());
assertEquals(expectedName, classType.getName());
return classType;
}
private ApiTypeInfo assertApi(TypeInfo type, String expectedName) {
assertEquals(ApiTypeInfo.class, type.getClass());
ApiTypeInfo apiType = (ApiTypeInfo) type;
assertEquals(ClassKind.API, apiType.getKind());
assertEquals(expectedName, apiType.getName());
return apiType;
}
private DataObjectTypeInfo assertDataObject(TypeInfo type, String expectedName, ClassKind expectedKind) {
assertEquals(DataObjectTypeInfo.class, type.getClass());
DataObjectTypeInfo classType = (DataObjectTypeInfo) type;
assertEquals(expectedKind, classType.getKind());
assertEquals(expectedName, classType.getName());
return classType;
}
private ParameterizedTypeInfo assertParameterized(TypeInfo type, String expectedName, ClassKind expectedKind) {
assertEquals(ParameterizedTypeInfo.class, type.getClass());
ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) type;
assertEquals(expectedKind, parameterizedType.getKind());
assertEquals(expectedName, parameterizedType.getName());
return parameterizedType;
}
private TypeInfo assertAsync(TypeInfo type, String name) {
String asyncName = "io.vertx.core.AsyncResult<" + name + ">";
ParameterizedTypeInfo handlerType = assertParameterized(assertHandler(type, asyncName), asyncName, ClassKind.ASYNC_RESULT);
ParameterizedTypeInfo resultType = assertParameterized(handlerType, asyncName, ClassKind.ASYNC_RESULT);
return resultType.getArg(0);
}
private TypeInfo assertHandler(TypeInfo type, String name) {
String handlerName = "io.vertx.core.Handler<" + name + ">";
ParameterizedTypeInfo handlerType = assertParameterized(type, handlerName, ClassKind.HANDLER);
return handlerType.getArg(0);
}
@Test
public void testCollection() throws Exception {
doTest(CollectionHolder.class, map -> {
String[] colTypes = { "list", "set", "map" };
ClassKind[] colKinds = { ClassKind.LIST, ClassKind.SET, ClassKind.MAP };
List<List<String>> colTypeParams = Arrays.asList(
Collections.singletonList("E"),
Collections.singletonList("E"),
Arrays.asList("K", "V"));
int[] typeParamIndexes = {0, 0, 1};
for (int idx = 0;idx < colKinds.length;idx++) {
String colType = colTypes[idx];
ClassKind colKind = colKinds[idx];
ClassTypeInfo col = (ClassTypeInfo) map.get(colType);
int typeParamIndex = typeParamIndexes[idx];
assertEquals(colKind, col.getKind());
assertEquals(ClassTypeInfo.class, col.getClass());
assertEquals(colTypeParams.get(idx), col.getParams().stream().map(TypeParamInfo::getName).collect(Collectors.toList()));
ParameterizedTypeInfo ofString = (ParameterizedTypeInfo) map.get(colType + "OfString");
assertEquals(colKind, ofString.getKind());
assertEquals(ParameterizedTypeInfo.class, ofString.getClass());
assertEquals(col, ofString.getRaw());
assertEquals(map.get("String"), ofString.getArg(typeParamIndex));
ParameterizedTypeInfo ofClassTypeParam = (ParameterizedTypeInfo) map.get(colType + "OfClassTypeParam");
assertEquals(colKind, ofClassTypeParam.getKind());
assertEquals(ParameterizedTypeInfo.class, ofClassTypeParam.getClass());
assertEquals(col, ofClassTypeParam.getRaw());
assertEquals(map.get("ClassTypeParam"), ofClassTypeParam.getArg(typeParamIndex));
ParameterizedTypeInfo ofMethodTypeParam = (ParameterizedTypeInfo) map.get(colType + "OfMethodTypeParam");
assertEquals(colKind, ofMethodTypeParam.getKind());
assertEquals(ParameterizedTypeInfo.class, ofMethodTypeParam.getClass());
assertEquals(col, ofMethodTypeParam.getRaw());
assertEquals(1 + typeParamIndex, ofMethodTypeParam.getArgs().size());
TypeParamInfo.Method methodTypeParam = (TypeParamInfo.Method) ((TypeVariableInfo) ofMethodTypeParam.getArg(typeParamIndex)).getParam();
assertEquals("MethodTypeParam", methodTypeParam.getName());
ParameterizedTypeInfo ofDataObject = (ParameterizedTypeInfo) map.get(colType + "OfDataObject");
assertEquals(colKind, ofDataObject.getKind());
assertEquals(ParameterizedTypeInfo.class, ofDataObject.getClass());
assertEquals(map.get("DataObject"), ofDataObject.getArg(typeParamIndex));
ParameterizedTypeInfo ofJsonObject = (ParameterizedTypeInfo) map.get(colType + "OfJsonObject");
assertEquals(colKind, ofJsonObject.getKind());
assertEquals(ParameterizedTypeInfo.class, ofJsonObject.getClass());
assertEquals(map.get("JsonObject"), ofJsonObject.getArg(typeParamIndex));
ParameterizedTypeInfo ofJsonArray = (ParameterizedTypeInfo) map.get(colType + "OfJsonArray");
assertEquals(colKind, ofJsonArray.getKind());
assertEquals(ParameterizedTypeInfo.class, ofJsonArray.getClass());
assertEquals(map.get("JsonArray"), ofJsonArray.getArg(typeParamIndex));
ParameterizedTypeInfo ofEnum = (ParameterizedTypeInfo) map.get(colType + "OfEnum");
assertEquals(colKind, ofEnum.getKind());
assertEquals(ParameterizedTypeInfo.class, ofEnum.getClass());
assertEquals(map.get("Enum"), ofEnum.getArg(typeParamIndex));
}
});
}
@Test
public void testStream() throws Exception {
doTest(StreamHolder.class, map -> {
ApiTypeInfo readStreamOfString = assertApi(assertParameterized(map.get("readStreamOfString"), "io.vertx.core.streams.ReadStream<java.lang.String>", ClassKind.API).getRaw(), "io.vertx.core.streams.ReadStream");
assertTypeVariable(readStreamOfString.getReadStreamArg(), "T");
ApiTypeInfo extendsReadStreamWithClassArg = assertApi(map.get("extendsReadStreamWithClassArg"), InterfaceExtentingReadStream.class.getName());
assertClass(extendsReadStreamWithClassArg.getReadStreamArg(), "java.lang.String", ClassKind.STRING);
ApiTypeInfo extendsGenericReadStreamSubTypeWithClassArg = assertApi(map.get("extendsGenericReadStreamSubTypeWithClassArg"), InterfaceSubtypingReadStream.class.getName());
assertClass(extendsGenericReadStreamSubTypeWithClassArg.getReadStreamArg(), "java.lang.String", ClassKind.STRING);
ApiTypeInfo genericReadStreamSubTypeWithClassTypeParamArg = assertApi(assertParameterized(map.get("genericReadStreamSubTypeWithClassTypeParamArg"), ReadStreamWithParameterizedTypeArg.class.getName() + "<ClassTypeParam>", ClassKind.API).getRaw(), "io.vertx.test.codegen.testapi.streams.ReadStreamWithParameterizedTypeArg");
// Cannot assert the correct value for now becasue Java does not provide enough info
});
}
@Test
public void testEnum() throws Exception {
doTest(EnumHolder.class, map -> {
EnumTypeInfo apiEnum = (EnumTypeInfo) map.get("apiEnum");
assertEquals(ClassKind.ENUM, apiEnum.getKind());
assertEquals(Arrays.asList("RED", "GREEN", "BLUE"), apiEnum.getValues());
assertTrue(apiEnum.isGen());
EnumTypeInfo otherEnum = (EnumTypeInfo) map.get("otherEnum");
assertEquals(ClassKind.ENUM, otherEnum.getKind());
assertEquals(Arrays.asList("NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS"), otherEnum.getValues());
assertFalse(otherEnum.isGen());
});
}
@Test
public void testGetErased() {
abstract class Container<M> implements AsyncResult<List<M>> {}
abstract class Expected implements AsyncResult<List<Object>> {}
ParameterizedTypeInfo info = (ParameterizedTypeInfo) TypeReflectionFactory.create(Container.class.getGenericInterfaces()[0]);
ParameterizedTypeInfo expected = (ParameterizedTypeInfo) TypeReflectionFactory.create(Expected.class.getGenericInterfaces()[0]);
assertEquals(expected, info.getErased());
}
}