/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.citrus.generictype; import static com.alibaba.citrus.generictype.TypeInfo.*; import static com.alibaba.citrus.test.TestUtil.*; import static org.junit.Assert.*; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.junit.Test; /** * 综合测试。 * * @author Michael Zhou */ public class TypeInfoFactoryTests { @Test(expected = IllegalArgumentException.class) public void getClass_array() { factory.getClassType(int[].class); } @Test public void getClass_rawClass() { assertSame(factory.getType(int.class), factory.getClassType(int.class)); assertSame(factory.getType(String.class), factory.getClassType(String.class)); assertSame(factory.getType(List.class), factory.getClassType(List.class)); assertSame(factory.getGenericDeclaration(int.class), factory.getClassType(int.class)); assertSame(factory.getGenericDeclaration(String.class), factory.getClassType(String.class)); assertSame(factory.getGenericDeclaration(List.class), factory.getClassType(List.class)); } @Test public void getClass_parameterizedClass() { class StringList extends ArrayList<String> { private static final long serialVersionUID = -6675085611077625191L; } ParameterizedType type = (ParameterizedType) StringList.class.getGenericSuperclass(); assertEquals(factory.getType(type), factory.getClassType(type)); } @Test(expected = IllegalArgumentException.class) public void getGenericDeclaration_arrayClass() { factory.getGenericDeclaration(Integer[][].class); } @Test public void getMethod() { for (Method method : TypeInfoFactoryTests.class.getMethods()) { assertEquals(factory.getGenericDeclaration(method), factory.getMethod(method)); } } @Test public void getConstructorMethod() { for (Constructor<?> constructor : ArrayList.class.getConstructors()) { assertEquals(factory.getGenericDeclaration(constructor), factory.getConstructor(constructor)); } } @Test public void getParameterizedType() { ParameterizedTypeInfo type1 = factory.getParameterizedType(List.class, String.class); ParameterizedTypeInfo type2 = factory.getParameterizedType(factory.getClassType(List.class), String.class); ParameterizedTypeInfo type3 = factory.getParameterizedType(factory.getClassType(List.class), factory.getClassType(String.class)); assertEquals(type1, type2); assertEquals(type2, type3); } @Test public void getArrayType() { ArrayTypeInfo ati1 = factory.getArrayType(String.class, 2); ArrayTypeInfo ati2 = factory.getArrayType(factory.getType(String.class), 2); assertSame(ati1, ati2); } /** 递归创建types的情形。 */ @Test public void recursiveType() { RawTypeInfo layout = (RawTypeInfo) factory.getType(Layout.class); ParameterizedTypeInfo pt_layout = (ParameterizedTypeInfo) layout.getTypeParameters().get(0).getBaseType(); assertSame(layout.getRawType(), pt_layout.getRawType()); RawTypeInfo layoutData = (RawTypeInfo) factory.getType(LayoutData.class); ParameterizedTypeInfo pt_layout2 = (ParameterizedTypeInfo) layoutData.getTypeParameters().get(0).getBaseType(); assertSame(pt_layout.getRawType(), pt_layout2.getRawType()); assertEquals("Layout<L>", layout.toString()); assertEquals("Layout<L=L>", pt_layout.toString()); assertEquals("LayoutData<L>", layoutData.toString()); assertEquals("Layout<L=L>", pt_layout2.toString()); } @Test public void recursiveMethod() throws Exception { MethodInfo method = (MethodInfo) factory.getGenericDeclaration(TypeInfoFactoryTests.class.getDeclaredMethod( "testMethod", List.class)); // Type Vars TypeVariableInfo varA = method.getTypeParameters().get(0); TypeVariableInfo varB = method.getTypeParameters().get(1); // 返回值类型 TypeVariableInfo returnType = (TypeVariableInfo) method.getReturnType(); assertEquals(varB, returnType); assertSame(method, returnType.getGenericDeclaration()); // 参数类型 ParameterizedTypeInfo paramType = (ParameterizedTypeInfo) method.getParameterTypes().get(0); TypeVariableInfo paramArgType = (TypeVariableInfo) paramType.getActualTypeArguments().get(0); assertEquals(varA, paramArgType); assertSame(method, paramArgType.getGenericDeclaration()); } @SuppressWarnings("unused") private <A, B extends Number> B testMethod(List<A> list) { return null; } @Test public void stressTest() throws Exception { File jarFile = new File(getJavaHome(), "jre/lib/rt.jar"); if (!jarFile.exists()) { jarFile = new File(getJavaHome(), "../Classes/classes.jar"); // mac style if (!jarFile.exists()) { throw new IllegalArgumentException("could not find jar file: jre/lib/rt.jar or classes.jar"); } } jarFile = jarFile.getCanonicalFile(); List<Class<?>> classes = getClasses(jarFile); long start = System.currentTimeMillis(); for (Class<?> c : classes) { TypeInfo type = factory.getType(c); System.out.println(type); } long duration = System.currentTimeMillis() - start; System.out.println("==========================="); System.out.printf("Total %,d classes in %,d ms, avg. %,.3f ms.\n", classes.size(), duration, (double) duration / classes.size()); } private List<Class<?>> getClasses(File jarFile) throws Exception { JarFile jar = new JarFile(jarFile); List<Class<?>> classes = new LinkedList<Class<?>>(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (Enumeration<JarEntry> i = jar.entries(); i.hasMoreElements(); ) { JarEntry entry = i.nextElement(); String name = entry.getName(); if (name.endsWith(".class")) { name = name.substring(0, name.length() - ".class".length()).replace('/', '.'); classes.add(Class.forName(name, false, loader)); } } jar.close(); return classes; } @Test public void resolve_includeBaseType() throws Exception { ClassTypeInfo myClassType = factory.getClassType(MyClass.class); ClassTypeInfo listAType = (ClassTypeInfo) factory.getType(MyClass.class.getField("listA").getGenericType()); // includeBaseType==false, Iterable<T=E> => Iterable<T=A> TypeInfo expectedType = factory.getParameterizedType(Iterable.class, MyClass.class.getTypeParameters()[0]); ClassTypeInfo iterableType = (ClassTypeInfo) factory.getType(List.class).getSupertype(Iterable.class); TypeInfo resolvedIterableType = iterableType.resolve(listAType, false); assertEquals(expectedType, resolvedIterableType); // resolvedIterableType应当还能再次resolve:Iterable<T=A> => Iterable<T=Integer> expectedType = factory.getParameterizedType(Iterable.class, Integer.class); ParameterizedTypeInfo context = factory.getParameterizedType(MyClass.class, Integer.class); assertEquals(expectedType, resolvedIterableType.resolve(context)); assertEquals(expectedType, resolvedIterableType.resolve(context, true)); assertEquals(expectedType, resolvedIterableType.resolve(context, false)); // includeBaseType==true, Iterable<T=E> => Iterable<T=Number> expectedType = factory.getParameterizedType(Iterable.class, Number.class); assertEquals(expectedType, iterableType.resolve(listAType)); assertEquals(expectedType, iterableType.resolve(listAType, true)); // includeBaseType==false, T => A TypeInfo varA = myClassType.getTypeParameters().get(0); TypeInfo varT = iterableType.getTypeParameters().get(0); assertEquals(varA, varT.resolve(listAType, false)); // includeBaseType==true, T => Number assertEquals(factory.getType(Number.class), varT.resolve(listAType)); assertEquals(factory.getType(Number.class), varT.resolve(listAType, true)); } @Test public void resolve_includeBaseType_2() throws Exception { ClassTypeInfo listAType = (ClassTypeInfo) factory.getType(MyClass2.class.getField("listA").getGenericType()); // includeBaseType==false, Iterable<T=Collection.E> => Iterable<T=List.E> TypeInfo expectedType = factory.getParameterizedType(Iterable.class, List.class.getTypeParameters()[0]); ClassTypeInfo iterableType = (ClassTypeInfo) factory.getType(List.class).getSupertype(Iterable.class); TypeInfo resolvedIterableType = iterableType.resolve(listAType, false); assertEquals(expectedType, resolvedIterableType); // resolvedIterableType应当还能再次resolve:Iterable<T=List.E> => Iterable<T=Integer> expectedType = factory.getParameterizedType(Iterable.class, Integer.class); ParameterizedTypeInfo context = factory.getParameterizedType(List.class, Integer.class); assertEquals(expectedType, resolvedIterableType.resolve(context)); assertEquals(expectedType, resolvedIterableType.resolve(context, true)); assertEquals(expectedType, resolvedIterableType.resolve(context, false)); // includeBaseType==true, Iterable<T=E> => Iterable<T=Object> expectedType = factory.getParameterizedType(Iterable.class, Object.class); assertEquals(expectedType, iterableType.resolve(listAType)); assertEquals(expectedType, iterableType.resolve(listAType, true)); // includeBaseType==false, T => List.E expectedType = factory.getType(List.class.getTypeParameters()[0]); TypeInfo varT = iterableType.getTypeParameters().get(0); assertEquals(expectedType, varT.resolve(listAType, false)); // includeBaseType==true, T => Object assertEquals(TypeInfo.OBJECT, varT.resolve(listAType)); assertEquals(TypeInfo.OBJECT, varT.resolve(listAType, true)); } } class MyClass<A extends Number> { public List<A> listA; } class MyClass2<A extends Number> { @SuppressWarnings("rawtypes") public List listA; } class Layout<L extends Layout<L>> { public LayoutData<L> data; } class LayoutData<L extends Layout<L>> { }