
1package org.junit.internal.runners.rules;2import org.junit.ClassRule;3import org.junit.Rule;4import org.junit.rules.MethodRule;5import org.junit.rules.TestRule;6import org.junit.runners.model.FrameworkMember;7import org.junit.runners.model.TestClass;8import java.lang.annotation.Annotation;9import java.lang.reflect.Modifier;10import java.util.ArrayList;11import java.util.List;12/**13 * A RuleMemberValidator validates the rule fields/methods of a14 * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the15 * {@code TestClass} are written to a list of errors.16 *17 * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR}18 * validates fields with a {@link ClassRule} annotation and the19 * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p>20 *21 * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR}22 * validates methods with a {@link ClassRule} annotation and the23 * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p>24 */25public class RuleMemberValidator {26    /**27     * Validates fields with a {@link ClassRule} annotation.28     */29    public static final RuleMemberValidator CLASS_RULE_VALIDATOR =30            classRuleValidatorBuilder()31            .withValidator(new DeclaringClassMustBePublic())32            .withValidator(new MemberMustBeStatic())33            .withValidator(new MemberMustBePublic())34            .withValidator(new FieldMustBeATestRule())35            .build();36    /**37     * Validates fields with a {@link Rule} annotation.38     */39    public static final RuleMemberValidator RULE_VALIDATOR =40            testRuleValidatorBuilder()41            .withValidator(new MemberMustBeNonStaticOrAlsoClassRule())42            .withValidator(new MemberMustBePublic())43            .withValidator(new FieldMustBeARule())44            .build();45    /**46     * Validates methods with a {@link ClassRule} annotation.47     */48    public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR =49            classRuleValidatorBuilder()50            .forMethods()51            .withValidator(new DeclaringClassMustBePublic())52            .withValidator(new MemberMustBeStatic())53            .withValidator(new MemberMustBePublic())54            .withValidator(new MethodMustBeATestRule())55            .build();56    /**57     * Validates methods with a {@link Rule} annotation.58     */59    public static final RuleMemberValidator RULE_METHOD_VALIDATOR =60            testRuleValidatorBuilder()61            .forMethods()62            .withValidator(new MemberMustBeNonStaticOrAlsoClassRule())63            .withValidator(new MemberMustBePublic())64            .withValidator(new MethodMustBeARule())65            .build();66    private final Class<? extends Annotation> annotation;67    private final boolean methods;68    private final List<RuleValidator> validatorStrategies;69    RuleMemberValidator(Builder builder) {70        this.annotation = builder.annotation;71        this.methods = builder.methods;72        this.validatorStrategies = builder.validators;73    }74    /**75     * Validate the {@link org.junit.runners.model.TestClass} and adds reasons76     * for rejecting the class to a list of errors.77     *78     * @param target the {@code TestClass} to validate.79     * @param errors the list of errors.80     */81    public void validate(TestClass target, List<Throwable> errors) {82        List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation)83                : target.getAnnotatedFields(annotation);84        for (FrameworkMember<?> each : members) {85            validateMember(each, errors);86        }87    }88    private void validateMember(FrameworkMember<?> member, List<Throwable> errors) {89        for (RuleValidator strategy : validatorStrategies) {90            strategy.validate(member, annotation, errors);91        }92    }93    private static Builder classRuleValidatorBuilder() {94        return new Builder(ClassRule.class);95    }96    private static Builder testRuleValidatorBuilder() {97        return new Builder(Rule.class);98    }99    private static class Builder {100        private final Class<? extends Annotation> annotation;101        private boolean methods;102        private final List<RuleValidator> validators;103        private Builder(Class<? extends Annotation> annotation) {104            this.annotation = annotation;105            this.methods = false;106            this.validators = new ArrayList<RuleValidator>();107        }108        Builder forMethods() {109            methods = true;110            return this;111        }112        Builder withValidator(RuleValidator validator) {113            validators.add(validator);114            return this;115        }116        RuleMemberValidator build() {117            return new RuleMemberValidator(this);118        }119    }120    private static boolean isRuleType(FrameworkMember<?> member) {121        return isMethodRule(member) || isTestRule(member);122    }123    private static boolean isTestRule(FrameworkMember<?> member) {124        return TestRule.class.isAssignableFrom(member.getType());125    }126    private static boolean isMethodRule(FrameworkMember<?> member) {127        return MethodRule.class.isAssignableFrom(member.getType());128    }129    /**130     * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and131     * {@link org.junit.ClassRule} annotations have been used correctly132     */133    interface RuleValidator {134        /**135         * Examine the given member and add any violations of the strategy's validation logic to the given list of errors136         * @param member The member (field or member) to examine137         * @param annotation The type of rule annotation on the member138         * @param errors The list of errors to add validation violations to139         */140        void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors);141    }142    /**143     * Requires the validated member to be non-static144     */145    private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator {146        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {147            boolean isMethodRuleMember = isMethodRule(member);148            boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null);149            // We disallow:150            //  - static MethodRule members151            //  - static @Rule annotated members152            //    - UNLESS they're also @ClassRule annotated153            // Note that MethodRule cannot be annotated with @ClassRule154            if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) {155                String message;156                if (isMethodRule(member)) {157                    message = "must not be static.";158                } else {159                    message = "must not be static or it must be annotated with @ClassRule.";160                }161                errors.add(new ValidationError(member, annotation, message));162            }163        }164    }165    /**166     * Requires the member to be static167     */168    private static final class MemberMustBeStatic implements RuleValidator {169        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {170            if (!member.isStatic()) {171                errors.add(new ValidationError(member, annotation,172                        "must be static."));173            }174        }175    }176    /**177     * Requires the member's declaring class to be public178     */179    private static final class DeclaringClassMustBePublic implements RuleValidator {180        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {181            if (!isDeclaringClassPublic(member)) {182                errors.add(new ValidationError(member, annotation,183                        "must be declared in a public class."));184            }185        }186        private boolean isDeclaringClassPublic(FrameworkMember<?> member) {187            return Modifier.isPublic(member.getDeclaringClass().getModifiers());188        }189    }190    /**191     * Requires the member to be public192     */193    private static final class MemberMustBePublic implements RuleValidator {194        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {195            if (!member.isPublic()) {196                errors.add(new ValidationError(member, annotation,197                        "must be public."));198            }199        }200    }201    /**202     * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule}203     */204    private static final class FieldMustBeARule implements RuleValidator {205        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {206            if (!isRuleType(member)) {207                errors.add(new ValidationError(member, annotation,208                        "must implement MethodRule or TestRule."));209            }210        }211    }212    /**213     * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or214     * {@link org.junit.rules.TestRule}215     */216    private static final class MethodMustBeARule implements RuleValidator {217        public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) {218            if (!isRuleType(member)) {219                errors.add(new ValidationError(member, annotation,220                        "must return an implementation of MethodRule or TestRule."));221            }222        }223    }224    225    /**226     * Require the member to return an implementation of {@link org.junit.rules.TestRule}227     */228    private static final class MethodMustBeATestRule implements RuleValidator {229        public void validate(FrameworkMember<?> member,230                Class<? extends Annotation> annotation, List<Throwable> errors) {231            if (!isTestRule(member)) {232                errors.add(new ValidationError(member, annotation, 233                        "must return an implementation of TestRule."));234            }235        }236    }237    238    /**239     * Requires the member is a field implementing {@link org.junit.rules.TestRule}240     */241    private static final class FieldMustBeATestRule implements RuleValidator {242        public void validate(FrameworkMember<?> member,243                Class<? extends Annotation> annotation, List<Throwable> errors) {244            if (!isTestRule(member)) {245                errors.add(new ValidationError(member, annotation,246                        "must implement TestRule."));247            }248        }249    }250}...
