001package org.junit.validator;
002
003import static java.util.Collections.singletonList;
004
005import java.lang.annotation.Annotation;
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.List;
009
010import org.junit.runners.model.Annotatable;
011import org.junit.runners.model.FrameworkField;
012import org.junit.runners.model.FrameworkMethod;
013import org.junit.runners.model.TestClass;
014
015/**
016 * An {@code AnnotationsValidator} validates all annotations of a test class,
017 * including its annotated fields and methods.
018 * 
019 * @since 4.12
020 */
021public final class AnnotationsValidator implements TestClassValidator {
022    private static final List<AnnotatableValidator<?>> VALIDATORS = Arrays.<AnnotatableValidator<?>>asList(
023            new ClassValidator(), new MethodValidator(), new FieldValidator());
024
025    /**
026     * Validate all annotations of the specified test class that are be
027     * annotated with {@link ValidateWith}.
028     * 
029     * @param testClass
030     *            the {@link TestClass} that is validated.
031     * @return the errors found by the validator.
032     */
033    public List<Exception> validateTestClass(TestClass testClass) {
034        List<Exception> validationErrors= new ArrayList<Exception>();
035        for (AnnotatableValidator<?> validator : VALIDATORS) {
036            List<Exception> additionalErrors= validator
037                    .validateTestClass(testClass);
038            validationErrors.addAll(additionalErrors);
039        }
040        return validationErrors;
041    }
042
043    private static abstract class AnnotatableValidator<T extends Annotatable> {
044        private static final AnnotationValidatorFactory ANNOTATION_VALIDATOR_FACTORY = new AnnotationValidatorFactory();
045
046        abstract Iterable<T> getAnnotatablesForTestClass(TestClass testClass);
047
048        abstract List<Exception> validateAnnotatable(
049                AnnotationValidator validator, T annotatable);
050
051        public List<Exception> validateTestClass(TestClass testClass) {
052            List<Exception> validationErrors= new ArrayList<Exception>();
053            for (T annotatable : getAnnotatablesForTestClass(testClass)) {
054                List<Exception> additionalErrors= validateAnnotatable(annotatable);
055                validationErrors.addAll(additionalErrors);
056            }
057            return validationErrors;
058        }
059
060        private List<Exception> validateAnnotatable(T annotatable) {
061            List<Exception> validationErrors= new ArrayList<Exception>();
062            for (Annotation annotation : annotatable.getAnnotations()) {
063                Class<? extends Annotation> annotationType = annotation
064                        .annotationType();
065                ValidateWith validateWith = annotationType
066                        .getAnnotation(ValidateWith.class);
067                if (validateWith != null) {
068                    AnnotationValidator annotationValidator = ANNOTATION_VALIDATOR_FACTORY
069                            .createAnnotationValidator(validateWith);
070                    List<Exception> errors= validateAnnotatable(
071                            annotationValidator, annotatable);
072                    validationErrors.addAll(errors);
073                }
074            }
075            return validationErrors;
076        }
077    }
078
079    private static class ClassValidator extends AnnotatableValidator<TestClass> {
080        @Override
081        Iterable<TestClass> getAnnotatablesForTestClass(TestClass testClass) {
082            return singletonList(testClass);
083        }
084
085        @Override
086        List<Exception> validateAnnotatable(
087                AnnotationValidator validator, TestClass testClass) {
088            return validator.validateAnnotatedClass(testClass);
089        }
090    }
091
092    private static class MethodValidator extends
093            AnnotatableValidator<FrameworkMethod> {
094        @Override
095        Iterable<FrameworkMethod> getAnnotatablesForTestClass(
096                TestClass testClass) {
097            return testClass.getAnnotatedMethods();
098        }
099
100        @Override
101        List<Exception> validateAnnotatable(
102                AnnotationValidator validator, FrameworkMethod method) {
103            return validator.validateAnnotatedMethod(method);
104        }
105    }
106
107    private static class FieldValidator extends
108            AnnotatableValidator<FrameworkField> {
109        @Override
110        Iterable<FrameworkField> getAnnotatablesForTestClass(TestClass testClass) {
111            return testClass.getAnnotatedFields();
112        }
113
114        @Override
115        List<Exception> validateAnnotatable(
116                AnnotationValidator validator, FrameworkField field) {
117            return validator.validateAnnotatedField(field);
118        }
119    };
120}