    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    }