1package junitparams.internal.parameters;
2
3import junitparams.Parameters;
4import org.junit.runners.model.FrameworkMethod;
5
6import java.lang.reflect.Method;
7import java.lang.reflect.Modifier;
8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.List;
11
12class ParametersFromExternalClassProvideMethod implements ParametrizationStrategy {
13    private final ParamsFromMethodCommon paramsFromMethodCommon;
14    private Parameters annotation;
15
16    ParametersFromExternalClassProvideMethod(FrameworkMethod frameworkMethod) {
17        this.paramsFromMethodCommon = new ParamsFromMethodCommon(frameworkMethod);
18        annotation = frameworkMethod.getAnnotation(Parameters.class);
19    }
20
21    @Override
22    public Object[] getParameters() {
23        Class<?> sourceClass = annotation.source();
24        return fillResultWithAllParamProviderMethods(sourceClass);
25    }
26
27    @Override
28    public boolean isApplicable() {
29        return annotation != null
30                && !annotation.source().isAssignableFrom(Void.class)
31                && annotation.method().isEmpty();
32    }
33
34    private Object[] fillResultWithAllParamProviderMethods(Class<?> sourceClass) {
35        if (sourceClass.isEnum()) {
36            return sourceClass.getEnumConstants();
37        }
38
39        List<Object> result = getParamsFromSourceHierarchy(sourceClass);
40        if (result.isEmpty())
41            throw new RuntimeException(
42                    "No methods starting with provide or they return no result in the parameters source class: "
43                            + sourceClass.getName());
44
45        return result.toArray();
46    }
47
48    private List<Object> getParamsFromSourceHierarchy(Class<?> sourceClass) {
49        List<Object> result = new ArrayList<Object>();
50        while (sourceClass.getSuperclass() != null) {
51            result.addAll(gatherParamsFromAllMethodsFrom(sourceClass));
52            sourceClass = sourceClass.getSuperclass();
53        }
54
55        return result;
56    }
57
58    private List<Object> gatherParamsFromAllMethodsFrom(Class<?> sourceClass) {
59        List<Object> result = new ArrayList<Object>();
60        Method[] methods = sourceClass.getDeclaredMethods();
61        for (Method prividerMethod : methods) {
62            if (prividerMethod.getName().startsWith("provide")) {
63                if (!Modifier.isStatic(prividerMethod.getModifiers())) {
64                    throw new RuntimeException("Parameters source method " +
65                            prividerMethod.getName() +
66                            " is not declared as static. Change it to a static method.");
67                }
68                try {
69                    result.addAll(
70                            Arrays.asList(paramsFromMethodCommon.getDataFromMethod(prividerMethod)));
71                } catch (Exception e) {
72                    throw new RuntimeException("Cannot invoke parameters source method: " + prividerMethod.getName(),
73                            e);
74                }
75            }
76        }
77        return result;
78    }
79}