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            if (isPublic()) {
033                // This method could be a public method in a package-scope base class
034                try {
035                    method.setAccessible(true);
036                } catch (SecurityException  e) {
037                    // We may get an IllegalAccessException when we try to call the method
038                }
039            }
040        }
041    
042        /**
043         * Returns the underlying Java method
044         */
045        public Method getMethod() {
046            return method;
047        }
048    
049        /**
050         * Returns the result of invoking this method on {@code target} with
051         * parameters {@code params}. {@link InvocationTargetException}s thrown are
052         * unwrapped, and their causes rethrown.
053         */
054        public Object invokeExplosively(final Object target, final Object... params)
055                throws Throwable {
056            return new ReflectiveCallable() {
057                @Override
058                protected Object runReflectiveCall() throws Throwable {
059                    return method.invoke(target, params);
060                }
061            }.run();
062        }
063    
064        /**
065         * Returns the method's name
066         */
067        @Override
068        public String getName() {
069            return method.getName();
070        }
071    
072        /**
073         * Adds to {@code errors} if this method:
074         * <ul>
075         * <li>is not public, or
076         * <li>takes parameters, or
077         * <li>returns something other than void, or
078         * <li>is static (given {@code isStatic is false}), or
079         * <li>is not static (given {@code isStatic is true}).
080         * </ul>
081         */
082        public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
083            validatePublicVoid(isStatic, errors);
084            if (method.getParameterTypes().length != 0) {
085                errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
086            }
087        }
088    
089    
090        /**
091         * Adds to {@code errors} if this method:
092         * <ul>
093         * <li>is not public, or
094         * <li>returns something other than void, or
095         * <li>is static (given {@code isStatic is false}), or
096         * <li>is not static (given {@code isStatic is true}).
097         * </ul>
098         */
099        public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
100            if (isStatic() != isStatic) {
101                String state = isStatic ? "should" : "should not";
102                errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
103            }
104            if (!isPublic()) {
105                errors.add(new Exception("Method " + method.getName() + "() should be public"));
106            }
107            if (method.getReturnType() != Void.TYPE) {
108                errors.add(new Exception("Method " + method.getName() + "() should be void"));
109            }
110        }
111    
112        @Override
113        protected int getModifiers() {
114            return method.getModifiers();
115        }
116    
117        /**
118         * Returns the return type of the method
119         */
120        public Class<?> getReturnType() {
121            return method.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        /**
133         * Returns the class where the method is actually declared
134         */
135        @Override
136        public Class<?> getDeclaringClass() {
137            return method.getDeclaringClass();
138        }
139    
140        public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
141            new NoGenericTypeParametersValidator(method).validate(errors);
142        }
143    
144        @Override
145        public boolean isShadowedBy(FrameworkMethod other) {
146            if (!other.getName().equals(getName())) {
147                return false;
148            }
149            if (other.getParameterTypes().length != getParameterTypes().length) {
150                return false;
151            }
152            for (int i = 0; i < other.getParameterTypes().length; i++) {
153                if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
154                    return false;
155                }
156            }
157            return true;
158        }
159    
160        @Override
161        boolean isBridgeMethod() {
162            return method.isBridge();
163        }
164    
165        @Override
166        public boolean equals(Object obj) {
167            if (!FrameworkMethod.class.isInstance(obj)) {
168                return false;
169            }
170            return ((FrameworkMethod) obj).method.equals(method);
171        }
172    
173        @Override
174        public int hashCode() {
175            return method.hashCode();
176        }
177    
178        /**
179         * Returns true if this is a no-arg method that returns a value assignable
180         * to {@code type}
181         *
182         * @deprecated This is used only by the Theories runner, and does not
183         *             use all the generic type info that it ought to. It will be replaced
184         *             with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
185         *             once Theories moves to junit-contrib.
186         */
187        @Deprecated
188        public boolean producesType(Type type) {
189            return getParameterTypes().length == 0 && type instanceof Class<?>
190                    && ((Class<?>) type).isAssignableFrom(method.getReturnType());
191        }
192    
193        private Class<?>[] getParameterTypes() {
194            return method.getParameterTypes();
195        }
196    
197        /**
198         * Returns the annotations on this method
199         */
200        public Annotation[] getAnnotations() {
201            return method.getAnnotations();
202        }
203    
204        /**
205         * Returns the annotation of type {@code annotationType} on this method, if
206         * one exists.
207         */
208        public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
209            return method.getAnnotation(annotationType);
210        }
211    
212        @Override
213        public String toString() {
214            return method.toString();
215        }
216    }