1fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinpackage junitparams.internal.parameters;
2fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
3fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport java.lang.reflect.InvocationTargetException;
4fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport java.lang.reflect.Method;
5fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport java.util.ArrayList;
6fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport java.util.Iterator;
7fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport java.util.List;
8fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
9fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport org.junit.runners.model.FrameworkMethod;
10fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
11fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinimport junitparams.Parameters;
12fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
13fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffinclass ParamsFromMethodCommon {
14fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private FrameworkMethod frameworkMethod;
15fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
16fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    ParamsFromMethodCommon(FrameworkMethod frameworkMethod) {
17fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        this.frameworkMethod = frameworkMethod;
18fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
19fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
20fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    Object[] paramsFromMethod(Class<?> sourceClass) {
21fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        String methodAnnotation = frameworkMethod.getAnnotation(Parameters.class).method();
22fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
23fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        if (methodAnnotation.isEmpty()) {
24fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            return invokeMethodWithParams(defaultMethodName(), sourceClass);
25fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
26fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
27fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        List<Object> result = new ArrayList<Object>();
28fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        for (String methodName : methodAnnotation.split(",")) {
29fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            for (Object param : invokeMethodWithParams(methodName.trim(), sourceClass))
30fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                result.add(param);
31fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
32fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
33fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return result.toArray();
34fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
35fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
36fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    Object[] getDataFromMethod(Method providerMethod) throws IllegalAccessException, InvocationTargetException {
37fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return encapsulateParamsIntoArrayIfSingleParamsetPassed((Object[]) providerMethod.invoke(null));
38fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
39fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
40fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    boolean containsDefaultParametersProvidingMethod(Class<?> sourceClass) {
41fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return findMethodInTestClassHierarchy(defaultMethodName(), sourceClass) != null;
42fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
43fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
44fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private String defaultMethodName() {
45fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return "parametersFor" + frameworkMethod.getName().substring(0, 1).toUpperCase()
46fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                + this.frameworkMethod.getName().substring(1);
47fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
48fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
49fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private Object[] invokeMethodWithParams(String methodName, Class<?> sourceClass) {
50fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        Method providerMethod = findMethodInTestClassHierarchy(methodName, sourceClass);
51fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        if (providerMethod == null) {
52fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            throw new RuntimeException("Could not find method: " + methodName + " so no params were used.");
53fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
54fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
55fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return invokeParamsProvidingMethod(providerMethod, sourceClass);
56fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
57fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
58fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    @SuppressWarnings("unchecked")
59fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private Object[] invokeParamsProvidingMethod(Method provideMethod, Class<?> sourceClass) {
60fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        try {
61fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            Object testObject = sourceClass.newInstance();
62fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            provideMethod.setAccessible(true);
63fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            Object result = provideMethod.invoke(testObject);
64fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
65fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            if (Object[].class.isAssignableFrom(result.getClass())) {
66fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                Object[] params = (Object[]) result;
67fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                return encapsulateParamsIntoArrayIfSingleParamsetPassed(params);
68fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            }
69fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
70fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            if (Iterable.class.isAssignableFrom(result.getClass())) {
71fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                try {
72fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    ArrayList<Object[]> res = new ArrayList<Object[]>();
73fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    for (Object[] paramSet : (Iterable<Object[]>) result)
74fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        res.add(paramSet);
75fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    return res.toArray();
76fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                } catch (ClassCastException e1) {
77fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    // Iterable with consecutive paramsets, each of one param
78fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    ArrayList<Object> res = new ArrayList<Object>();
79fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    for (Object param : (Iterable<?>) result)
80fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        res.add(new Object[]{param});
81fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    return res.toArray();
82fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                }
83fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            }
84fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
85fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            if (Iterator.class.isAssignableFrom(result.getClass())) {
86fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                Object iteratedElement = null;
87fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                try {
88fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    ArrayList<Object[]> res = new ArrayList<Object[]>();
89fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    Iterator<Object[]> iterator = (Iterator<Object[]>) result;
90fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    while (iterator.hasNext()) {
91fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        iteratedElement = iterator.next();
92fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        // ClassCastException will occur in the following line
93fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        // if the iterator is actually Iterator<Object> in Java 7
94fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        res.add((Object[]) iteratedElement);
95fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    }
96fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    return res.toArray();
97fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                } catch (ClassCastException e1) {
98fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    // Iterator with consecutive paramsets, each of one param
99fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    ArrayList<Object> res = new ArrayList<Object>();
100fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    Iterator<?> iterator = (Iterator<?>) result;
101fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    // The first element is already stored in iteratedElement
102fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    res.add(iteratedElement);
103fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    while (iterator.hasNext()) {
104fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                        res.add(new Object[]{iterator.next()});
105fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    }
106fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    return res.toArray();
107fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                }
108fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            }
109fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
110fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            throw new ClassCastException();
111fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
112fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        } catch (ClassCastException e) {
113fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            throw new RuntimeException("The return type of: " + provideMethod.getName() + " defined in class " +
114fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    sourceClass + " is not Object[][] nor Iterable<Object[]>. Fix it!", e);
115fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        } catch (Exception e) {
116fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            throw new RuntimeException("Could not invoke method: " + provideMethod.getName() + " defined in class " +
117fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                    sourceClass + " so no params were used.", e);
118fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
119fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
120fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
121fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private Method findMethodInTestClassHierarchy(String methodName, Class<?> sourceClass) {
122fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        Class<?> declaringClass = sourceClass;
123fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        while (declaringClass.getSuperclass() != null) {
124fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            try {
125fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin                return declaringClass.getDeclaredMethod(methodName);
126fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            } catch (Exception ignore) {
127fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            }
128fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            declaringClass = declaringClass.getSuperclass();
129fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
130fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return null;
131fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
132fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
133fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    private Object[] encapsulateParamsIntoArrayIfSingleParamsetPassed(Object[] params) {
134fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        if (frameworkMethod.getMethod().getParameterTypes().length != params.length) {
135fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            return params;
136fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
137fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
138fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        if (params.length == 0) {
139fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            return params;
140fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
141fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
142fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        Object param = params[0];
143fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        if (param == null || !param.getClass().isArray()) {
144fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin            return new Object[]{params};
145fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        }
146fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
147fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin        return params;
148fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin    }
149fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin
150fd1f9491413fcdbfae3cbd43651db31fdabce41aPaul Duffin}
151