1package org.junit.runners.model;
2
3import java.lang.annotation.Annotation;
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6import java.lang.reflect.Modifier;
7import java.lang.reflect.Type;
8import java.util.List;
9
10import org.junit.internal.runners.model.ReflectiveCallable;
11
12/**
13 * Represents a method on a test class to be invoked at the appropriate point in
14 * test execution. These methods are usually marked with an annotation (such as
15 * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass},
16 * {@code @AfterClass}, etc.)
17 */
18public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
19	final Method fMethod;
20
21	/**
22	 * Returns a new {@code FrameworkMethod} for {@code method}
23	 */
24	public FrameworkMethod(Method method) {
25		fMethod= method;
26	}
27
28	/**
29	 * Returns the underlying Java method
30	 */
31	public Method getMethod() {
32		return fMethod;
33	}
34
35	/**
36	 * Returns the result of invoking this method on {@code target} with
37	 * parameters {@code params}. {@link InvocationTargetException}s thrown are
38	 * unwrapped, and their causes rethrown.
39	 */
40	public Object invokeExplosively(final Object target, final Object... params)
41			throws Throwable {
42		return new ReflectiveCallable() {
43			@Override
44			protected Object runReflectiveCall() throws Throwable {
45				return fMethod.invoke(target, params);
46			}
47		}.run();
48	}
49
50	/**
51	 * Returns the method's name
52	 */
53	public String getName() {
54		return fMethod.getName();
55	}
56
57	/**
58	 * Adds to {@code errors} if this method:
59	 * <ul>
60	 * <li>is not public, or
61	 * <li>takes parameters, or
62	 * <li>returns something other than void, or
63	 * <li>is static (given {@code isStatic is false}), or
64	 * <li>is not static (given {@code isStatic is true}).
65	 */
66	public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
67		validatePublicVoid(isStatic, errors);
68		if (fMethod.getParameterTypes().length != 0)
69			errors.add(new Exception("Method " + fMethod.getName() + " should have no parameters"));
70	}
71
72
73	/**
74	 * Adds to {@code errors} if this method:
75	 * <ul>
76	 * <li>is not public, or
77	 * <li>returns something other than void, or
78	 * <li>is static (given {@code isStatic is false}), or
79	 * <li>is not static (given {@code isStatic is true}).
80	 */
81	public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
82		if (Modifier.isStatic(fMethod.getModifiers()) != isStatic) {
83			String state= isStatic ? "should" : "should not";
84			errors.add(new Exception("Method " + fMethod.getName() + "() " + state + " be static"));
85		}
86		if (!Modifier.isPublic(fMethod.getDeclaringClass().getModifiers()))
87			errors.add(new Exception("Class " + fMethod.getDeclaringClass().getName() + " should be public"));
88		if (!Modifier.isPublic(fMethod.getModifiers()))
89			errors.add(new Exception("Method " + fMethod.getName() + "() should be public"));
90		if (fMethod.getReturnType() != Void.TYPE)
91			errors.add(new Exception("Method " + fMethod.getName() + "() should be void"));
92	}
93
94	public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
95		new NoGenericTypeParametersValidator(fMethod).validate(errors);
96	}
97
98	@Override
99	public boolean isShadowedBy(FrameworkMethod other) {
100		if (!other.getName().equals(getName()))
101			return false;
102		if (other.getParameterTypes().length != getParameterTypes().length)
103			return false;
104		for (int i= 0; i < other.getParameterTypes().length; i++)
105			if (!other.getParameterTypes()[i].equals(getParameterTypes()[i]))
106				return false;
107		return true;
108	}
109
110	@Override
111	public boolean equals(Object obj) {
112		if (!FrameworkMethod.class.isInstance(obj))
113			return false;
114		return ((FrameworkMethod) obj).fMethod.equals(fMethod);
115	}
116
117	@Override
118	public int hashCode() {
119		return fMethod.hashCode();
120	}
121
122	/**
123	 * Returns true iff this is a no-arg method that returns a value assignable
124	 * to {@code type}
125	 *
126	 * @deprecated This is used only by the Theories runner, and does not
127	 * use all the generic type info that it ought to. It will be replaced
128	 * with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
129	 * once Theories moves to junit-contrib.
130	 */
131	@Deprecated
132	public boolean producesType(Type type) {
133		return getParameterTypes().length == 0 && type instanceof Class<?>
134		    && ((Class<?>) type).isAssignableFrom(fMethod.getReturnType());
135	}
136
137	private Class<?>[] getParameterTypes() {
138		return fMethod.getParameterTypes();
139	}
140
141	/**
142	 * Returns the annotations on this method
143	 */
144	@Override
145	public Annotation[] getAnnotations() {
146		return fMethod.getAnnotations();
147	}
148
149	/**
150	 * Returns the annotation of type {@code annotationType} on this method, if
151	 * one exists.
152	 */
153	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
154		return fMethod.getAnnotation(annotationType);
155	}
156}
157