001    package org.junit.runners.model;
002    
003    import java.lang.annotation.Annotation;
004    import java.lang.reflect.InvocationTargetException;
005    import java.lang.reflect.Method;
006    import java.lang.reflect.Type;
007    import java.util.List;
008    
009    import 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     */
019    public 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    }