001    package org.junit.runners.parameterized;
002    
003    import java.lang.annotation.Annotation;
004    import java.lang.reflect.Field;
005    import java.util.List;
006    
007    import org.junit.runner.notification.RunNotifier;
008    import org.junit.runners.BlockJUnit4ClassRunner;
009    import org.junit.runners.Parameterized.Parameter;
010    import org.junit.runners.model.FrameworkField;
011    import org.junit.runners.model.FrameworkMethod;
012    import org.junit.runners.model.InitializationError;
013    import org.junit.runners.model.Statement;
014    
015    /**
016     * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be
017     * injected via constructor or into annotated fields.
018     */
019    public class BlockJUnit4ClassRunnerWithParameters extends
020            BlockJUnit4ClassRunner {
021        private final Object[] parameters;
022    
023        private final String name;
024    
025        public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)
026                throws InitializationError {
027            super(test.getTestClass().getJavaClass());
028            parameters = test.getParameters().toArray(
029                    new Object[test.getParameters().size()]);
030            name = test.getName();
031        }
032    
033        @Override
034        public Object createTest() throws Exception {
035            if (fieldsAreAnnotated()) {
036                return createTestUsingFieldInjection();
037            } else {
038                return createTestUsingConstructorInjection();
039            }
040        }
041    
042        private Object createTestUsingConstructorInjection() throws Exception {
043            return getTestClass().getOnlyConstructor().newInstance(parameters);
044        }
045    
046        private Object createTestUsingFieldInjection() throws Exception {
047            List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
048            if (annotatedFieldsByParameter.size() != parameters.length) {
049                throw new Exception(
050                        "Wrong number of parameters and @Parameter fields."
051                                + " @Parameter fields counted: "
052                                + annotatedFieldsByParameter.size()
053                                + ", available parameters: " + parameters.length
054                                + ".");
055            }
056            Object testClassInstance = getTestClass().getJavaClass().newInstance();
057            for (FrameworkField each : annotatedFieldsByParameter) {
058                Field field = each.getField();
059                Parameter annotation = field.getAnnotation(Parameter.class);
060                int index = annotation.value();
061                try {
062                    field.set(testClassInstance, parameters[index]);
063                } catch (IllegalArgumentException iare) {
064                    throw new Exception(getTestClass().getName()
065                            + ": Trying to set " + field.getName()
066                            + " with the value " + parameters[index]
067                            + " that is not the right type ("
068                            + parameters[index].getClass().getSimpleName()
069                            + " instead of " + field.getType().getSimpleName()
070                            + ").", iare);
071                }
072            }
073            return testClassInstance;
074        }
075    
076        @Override
077        protected String getName() {
078            return name;
079        }
080    
081        @Override
082        protected String testName(FrameworkMethod method) {
083            return method.getName() + getName();
084        }
085    
086        @Override
087        protected void validateConstructor(List<Throwable> errors) {
088            validateOnlyOneConstructor(errors);
089            if (fieldsAreAnnotated()) {
090                validateZeroArgConstructor(errors);
091            }
092        }
093    
094        @Override
095        protected void validateFields(List<Throwable> errors) {
096            super.validateFields(errors);
097            if (fieldsAreAnnotated()) {
098                List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
099                int[] usedIndices = new int[annotatedFieldsByParameter.size()];
100                for (FrameworkField each : annotatedFieldsByParameter) {
101                    int index = each.getField().getAnnotation(Parameter.class)
102                            .value();
103                    if (index < 0 || index > annotatedFieldsByParameter.size() - 1) {
104                        errors.add(new Exception("Invalid @Parameter value: "
105                                + index + ". @Parameter fields counted: "
106                                + annotatedFieldsByParameter.size()
107                                + ". Please use an index between 0 and "
108                                + (annotatedFieldsByParameter.size() - 1) + "."));
109                    } else {
110                        usedIndices[index]++;
111                    }
112                }
113                for (int index = 0; index < usedIndices.length; index++) {
114                    int numberOfUse = usedIndices[index];
115                    if (numberOfUse == 0) {
116                        errors.add(new Exception("@Parameter(" + index
117                                + ") is never used."));
118                    } else if (numberOfUse > 1) {
119                        errors.add(new Exception("@Parameter(" + index
120                                + ") is used more than once (" + numberOfUse + ")."));
121                    }
122                }
123            }
124        }
125    
126        @Override
127        protected Statement classBlock(RunNotifier notifier) {
128            return childrenInvoker(notifier);
129        }
130    
131        @Override
132        protected Annotation[] getRunnerAnnotations() {
133            return new Annotation[0];
134        }
135    
136        private List<FrameworkField> getAnnotatedFieldsByParameter() {
137            return getTestClass().getAnnotatedFields(Parameter.class);
138        }
139    
140        private boolean fieldsAreAnnotated() {
141            return !getAnnotatedFieldsByParameter().isEmpty();
142        }
143    }