001package org.junit.runners.model;
002
003import java.lang.annotation.Annotation;
004import java.lang.reflect.InvocationTargetException;
005import java.lang.reflect.Method;
006import java.lang.reflect.Type;
007import java.util.List;
008
009import org.junit.internal.runners.model.ReflectiveCallable;
010
011/**
012 * Represents a method on a test class to be invoked at the appropriate point in
013 * test execution. These methods are usually marked with an annotation (such as
014 * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass},
015 * {@code @AfterClass}, etc.)
016 *
017 * @since 4.5
018 */
019public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
020    private final Method method;
021
022    /**
023     * Returns a new {@code FrameworkMethod} for {@code method}
024     */
025    public FrameworkMethod(Method method) {
026        if (method == null) {
027            throw new NullPointerException(
028                    "FrameworkMethod cannot be created without an underlying method.");
029        }
030        this.method = method;
031    }
032
033    /**
034     * Returns the underlying Java method
035     */
036    public Method getMethod() {
037        return method;
038    }
039
040    /**
041     * Returns the result of invoking this method on {@code target} with
042     * parameters {@code params}. {@link InvocationTargetException}s thrown are
043     * unwrapped, and their causes rethrown.
044     */
045    public Object invokeExplosively(final Object target, final Object... params)
046            throws Throwable {
047        return new ReflectiveCallable() {
048            @Override
049            protected Object runReflectiveCall() throws Throwable {
050                return method.invoke(target, params);
051            }
052        }.run();
053    }
054
055    /**
056     * Returns the method's name
057     */
058    @Override
059    public String getName() {
060        return method.getName();
061    }
062
063    /**
064     * Adds to {@code errors} if this method:
065     * <ul>
066     * <li>is not public, or
067     * <li>takes parameters, or
068     * <li>returns something other than void, or
069     * <li>is static (given {@code isStatic is false}), or
070     * <li>is not static (given {@code isStatic is true}).
071     * </ul>
072     */
073    public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
074        validatePublicVoid(isStatic, errors);
075        if (method.getParameterTypes().length != 0) {
076            errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
077        }
078    }
079
080
081    /**
082     * Adds to {@code errors} if this method:
083     * <ul>
084     * <li>is not public, or
085     * <li>returns something other than void, or
086     * <li>is static (given {@code isStatic is false}), or
087     * <li>is not static (given {@code isStatic is true}).
088     * </ul>
089     */
090    public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
091        if (isStatic() != isStatic) {
092            String state = isStatic ? "should" : "should not";
093            errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
094        }
095        if (!isPublic()) {
096            errors.add(new Exception("Method " + method.getName() + "() should be public"));
097        }
098        if (method.getReturnType() != Void.TYPE) {
099            errors.add(new Exception("Method " + method.getName() + "() should be void"));
100        }
101    }
102
103    @Override
104    protected int getModifiers() {
105        return method.getModifiers();
106    }
107
108    /**
109     * Returns the return type of the method
110     */
111    public Class<?> getReturnType() {
112        return method.getReturnType();
113    }
114
115    /**
116     * Returns the return type of the method
117     */
118    @Override
119    public Class<?> getType() {
120        return getReturnType();
121    }
122
123    /**
124     * Returns the class where the method is actually declared
125     */
126    @Override
127    public Class<?> getDeclaringClass() {
128        return method.getDeclaringClass();
129    }
130
131    public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
132        new NoGenericTypeParametersValidator(method).validate(errors);
133    }
134
135    @Override
136    public boolean isShadowedBy(FrameworkMethod other) {
137        if (!other.getName().equals(getName())) {
138            return false;
139        }
140        if (other.getParameterTypes().length != getParameterTypes().length) {
141            return false;
142        }
143        for (int i = 0; i < other.getParameterTypes().length; i++) {
144            if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
145                return false;
146            }
147        }
148        return true;
149    }
150
151    @Override
152    public boolean equals(Object obj) {
153        if (!FrameworkMethod.class.isInstance(obj)) {
154            return false;
155        }
156        return ((FrameworkMethod) obj).method.equals(method);
157    }
158
159    @Override
160    public int hashCode() {
161        return method.hashCode();
162    }
163
164    /**
165     * Returns true if this is a no-arg method that returns a value assignable
166     * to {@code type}
167     *
168     * @deprecated This is used only by the Theories runner, and does not
169     *             use all the generic type info that it ought to. It will be replaced
170     *             with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
171     *             once Theories moves to junit-contrib.
172     */
173    @Deprecated
174    public boolean producesType(Type type) {
175        return getParameterTypes().length == 0 && type instanceof Class<?>
176                && ((Class<?>) type).isAssignableFrom(method.getReturnType());
177    }
178
179    private Class<?>[] getParameterTypes() {
180        return method.getParameterTypes();
181    }
182
183    /**
184     * Returns the annotations on this method
185     */
186    public Annotation[] getAnnotations() {
187        return method.getAnnotations();
188    }
189
190    /**
191     * Returns the annotation of type {@code annotationType} on this method, if
192     * one exists.
193     */
194    public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
195        return method.getAnnotation(annotationType);
196    }
197
198    @Override
199    public String toString() {
200        return method.toString();
201    }
202}