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