package org.junit.runners.model; import static java.lang.reflect.Modifier.isStatic; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; /** * Wraps a class to be run, providing method validation and annotation searching */ public class TestClass { private final Class fClass; private Map, List> fMethodsForAnnotations= new HashMap, List>(); private Map, List> fFieldsForAnnotations= new HashMap, List>(); /** * Creates a {@code TestClass} wrapping {@code klass}. Each time this * constructor executes, the class is scanned for annotations, which can be * an expensive process (we hope in future JDK's it will not be.) Therefore, * try to share instances of {@code TestClass} where possible. */ public TestClass(Class klass) { fClass= klass; if (klass != null && klass.getConstructors().length > 1) throw new IllegalArgumentException( "Test class can only have one constructor"); for (Class eachClass : getSuperClasses(fClass)) { for (Method eachMethod : eachClass.getDeclaredMethods()) addToAnnotationLists(new FrameworkMethod(eachMethod), fMethodsForAnnotations); for (Field eachField : eachClass.getDeclaredFields()) addToAnnotationLists(new FrameworkField(eachField), fFieldsForAnnotations); } } private > void addToAnnotationLists(T member, Map, List> map) { for (Annotation each : member.getAnnotations()) { Class type= each.annotationType(); List members= getAnnotatedMembers(map, type); if (member.isShadowedBy(members)) return; if (runsTopToBottom(type)) members.add(0, member); else members.add(member); } } /** * Returns, efficiently, all the non-overridden methods in this class and * its superclasses that are annotated with {@code annotationClass}. */ public List getAnnotatedMethods( Class annotationClass) { return getAnnotatedMembers(fMethodsForAnnotations, annotationClass); } /** * Returns, efficiently, all the non-overridden fields in this class and its * superclasses that are annotated with {@code annotationClass}. */ public List getAnnotatedFields( Class annotationClass) { return getAnnotatedMembers(fFieldsForAnnotations, annotationClass); } private List getAnnotatedMembers(Map, List> map, Class type) { if (!map.containsKey(type)) map.put(type, new ArrayList()); return map.get(type); } private boolean runsTopToBottom(Class annotation) { return annotation.equals(Before.class) || annotation.equals(BeforeClass.class); } private List> getSuperClasses(Class testClass) { ArrayList> results= new ArrayList>(); Class current= testClass; while (current != null) { results.add(current); current= current.getSuperclass(); } return results; } /** * Returns the underlying Java class. */ public Class getJavaClass() { return fClass; } /** * Returns the class's name. */ public String getName() { if (fClass == null) return "null"; return fClass.getName(); } /** * Returns the only public constructor in the class, or throws an {@code * AssertionError} if there are more or less than one. */ public Constructor getOnlyConstructor() { Constructor[] constructors= fClass.getConstructors(); Assert.assertEquals(1, constructors.length); return constructors[0]; } /** * Returns the annotations on this class */ public Annotation[] getAnnotations() { if (fClass == null) return new Annotation[0]; return fClass.getAnnotations(); } public List getAnnotatedFieldValues(Object test, Class annotationClass, Class valueClass) { List results= new ArrayList(); for (FrameworkField each : getAnnotatedFields(annotationClass)) { try { Object fieldValue= each.get(test); if (valueClass.isInstance(fieldValue)) results.add(valueClass.cast(fieldValue)); } catch (IllegalAccessException e) { throw new RuntimeException( "How did getFields return a field we couldn't access?", e); } } return results; } public boolean isANonStaticInnerClass() { return fClass.isMemberClass() && !isStatic(fClass.getModifiers()); } }