1b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotpackage org.junit.runners.model;
2b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
3b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport static java.lang.reflect.Modifier.isStatic;
4b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
5b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.annotation.Annotation;
6b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.reflect.Constructor;
7b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.reflect.Field;
8b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.reflect.Method;
9b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.util.ArrayList;
10b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.util.HashMap;
11b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.util.List;
12b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.util.Map;
13b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
14b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.Assert;
15b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.Before;
16b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.BeforeClass;
17b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
18b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot/**
19b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * Wraps a class to be run, providing method validation and annotation searching
20b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot */
21b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotpublic class TestClass {
22b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private final Class<?> fClass;
23b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
24b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations= new HashMap<Class<?>, List<FrameworkMethod>>();
25b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
26b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations= new HashMap<Class<?>, List<FrameworkField>>();
27b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
28b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
29b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Creates a {@code TestClass} wrapping {@code klass}. Each time this
30b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * constructor executes, the class is scanned for annotations, which can be
31b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * an expensive process (we hope in future JDK's it will not be.) Therefore,
32b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * try to share instances of {@code TestClass} where possible.
33b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
34b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public TestClass(Class<?> klass) {
35b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		fClass= klass;
36b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		if (klass != null && klass.getConstructors().length > 1)
37b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			throw new IllegalArgumentException(
38b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot					"Test class can only have one constructor");
39b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
40b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		for (Class<?> eachClass : getSuperClasses(fClass)) {
41b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			for (Method eachMethod : eachClass.getDeclaredMethods())
42b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				addToAnnotationLists(new FrameworkMethod(eachMethod),
43b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot						fMethodsForAnnotations);
44b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			for (Field eachField : eachClass.getDeclaredFields())
45b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				addToAnnotationLists(new FrameworkField(eachField),
46b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot						fFieldsForAnnotations);
47b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		}
48b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
49b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
50b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private <T extends FrameworkMember<T>> void addToAnnotationLists(T member,
51b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Map<Class<?>, List<T>> map) {
52b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		for (Annotation each : member.getAnnotations()) {
53b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Class<? extends Annotation> type= each.annotationType();
54b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			List<T> members= getAnnotatedMembers(map, type);
55b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			if (member.isShadowedBy(members))
56b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				return;
57b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			if (runsTopToBottom(type))
58b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				members.add(0, member);
59b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			else
60b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				members.add(member);
61b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		}
62b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
63b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
64b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
65b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns, efficiently, all the non-overridden methods in this class and
66b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * its superclasses that are annotated with {@code annotationClass}.
67b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
68b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public List<FrameworkMethod> getAnnotatedMethods(
69b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Class<? extends Annotation> annotationClass) {
70b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return getAnnotatedMembers(fMethodsForAnnotations, annotationClass);
71b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
72b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
73b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
74b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns, efficiently, all the non-overridden fields in this class and its
75b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * superclasses that are annotated with {@code annotationClass}.
76b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
77b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public List<FrameworkField> getAnnotatedFields(
78b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Class<? extends Annotation> annotationClass) {
79b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return getAnnotatedMembers(fFieldsForAnnotations, annotationClass);
80b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
81b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
82b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private <T> List<T> getAnnotatedMembers(Map<Class<?>, List<T>> map,
83b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Class<? extends Annotation> type) {
84b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		if (!map.containsKey(type))
85b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			map.put(type, new ArrayList<T>());
86b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return map.get(type);
87b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
88b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
89b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private boolean runsTopToBottom(Class<? extends Annotation> annotation) {
90b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return annotation.equals(Before.class)
91b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				|| annotation.equals(BeforeClass.class);
92b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
93b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
94b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	private List<Class<?>> getSuperClasses(Class<?> testClass) {
95b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		ArrayList<Class<?>> results= new ArrayList<Class<?>>();
96b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		Class<?> current= testClass;
97b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		while (current != null) {
98b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			results.add(current);
99b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			current= current.getSuperclass();
100b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		}
101b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return results;
102b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
103b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
104b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
105b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns the underlying Java class.
106b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
107b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public Class<?> getJavaClass() {
108b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return fClass;
109b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
110b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
111b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
112b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns the class's name.
113b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
114b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public String getName() {
115b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		if (fClass == null)
116b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			return "null";
117b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return fClass.getName();
118b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
119b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
120b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
121b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns the only public constructor in the class, or throws an {@code
122b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * AssertionError} if there are more or less than one.
123b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
124b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
125b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public Constructor<?> getOnlyConstructor() {
126b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		Constructor<?>[] constructors= fClass.getConstructors();
127b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		Assert.assertEquals(1, constructors.length);
128b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return constructors[0];
129b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
130b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
131b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	/**
132b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 * Returns the annotations on this class
133b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	 */
134b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public Annotation[] getAnnotations() {
135b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		if (fClass == null)
136b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			return new Annotation[0];
137b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return fClass.getAnnotations();
138b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
139b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
140b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public <T> List<T> getAnnotatedFieldValues(Object test,
141b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			Class<? extends Annotation> annotationClass, Class<T> valueClass) {
142b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		List<T> results= new ArrayList<T>();
143b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		for (FrameworkField each : getAnnotatedFields(annotationClass)) {
144b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			try {
145b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				Object fieldValue= each.get(test);
146b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				if (valueClass.isInstance(fieldValue))
147b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot					results.add(valueClass.cast(fieldValue));
148b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			} catch (IllegalAccessException e) {
149b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot				throw new RuntimeException(
150b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot						"How did getFields return a field we couldn't access?", e);
151b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot			}
152b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		}
153b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return results;
154b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
155b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot
156b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	public boolean isANonStaticInnerClass() {
157b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot		return fClass.isMemberClass() && !isStatic(fClass.getModifiers());
158b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot	}
159b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot}
160