1/**
2 *
3 */
4package org.junit.experimental.theories.internal;
5
6import java.lang.reflect.Array;
7import java.lang.reflect.Field;
8import java.lang.reflect.Modifier;
9import java.util.ArrayList;
10import java.util.List;
11
12import org.junit.experimental.theories.DataPoint;
13import org.junit.experimental.theories.DataPoints;
14import org.junit.experimental.theories.ParameterSignature;
15import org.junit.experimental.theories.ParameterSupplier;
16import org.junit.experimental.theories.PotentialAssignment;
17import org.junit.runners.model.FrameworkMethod;
18import org.junit.runners.model.TestClass;
19
20/**
21 * Supplies Theory parameters based on all public members of the target class.
22 */
23public class AllMembersSupplier extends ParameterSupplier {
24	static class MethodParameterValue extends PotentialAssignment {
25		private final FrameworkMethod fMethod;
26
27		private MethodParameterValue(FrameworkMethod dataPointMethod) {
28			fMethod= dataPointMethod;
29		}
30
31		@Override
32		public Object getValue() throws CouldNotGenerateValueException {
33			try {
34				return fMethod.invokeExplosively(null);
35			} catch (IllegalArgumentException e) {
36				throw new RuntimeException(
37						"unexpected: argument length is checked");
38			} catch (IllegalAccessException e) {
39				throw new RuntimeException(
40						"unexpected: getMethods returned an inaccessible method");
41			} catch (Throwable e) {
42				throw new CouldNotGenerateValueException();
43				// do nothing, just look for more values
44			}
45		}
46
47		@Override
48		public String getDescription() throws CouldNotGenerateValueException {
49			return fMethod.getName();
50		}
51	}
52
53	private final TestClass fClass;
54
55	/**
56	 * Constructs a new supplier for {@code type}
57	 */
58	public AllMembersSupplier(TestClass type) {
59		fClass= type;
60	}
61
62	@Override
63	public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
64		List<PotentialAssignment> list= new ArrayList<PotentialAssignment>();
65
66		addFields(sig, list);
67		addSinglePointMethods(sig, list);
68		addMultiPointMethods(list);
69
70		return list;
71	}
72
73	private void addMultiPointMethods(List<PotentialAssignment> list) {
74		for (FrameworkMethod dataPointsMethod : fClass
75				.getAnnotatedMethods(DataPoints.class))
76			try {
77				addArrayValues(dataPointsMethod.getName(), list, dataPointsMethod.invokeExplosively(null));
78			} catch (Throwable e) {
79				// ignore and move on
80			}
81	}
82
83	@SuppressWarnings("deprecation")
84	private void addSinglePointMethods(ParameterSignature sig,
85			List<PotentialAssignment> list) {
86		for (FrameworkMethod dataPointMethod : fClass
87				.getAnnotatedMethods(DataPoint.class)) {
88			Class<?> type= sig.getType();
89			if ((dataPointMethod.producesType(type)))
90				list.add(new MethodParameterValue(dataPointMethod));
91		}
92	}
93
94	private void addFields(ParameterSignature sig,
95			List<PotentialAssignment> list) {
96		for (final Field field : fClass.getJavaClass().getFields()) {
97			if (Modifier.isStatic(field.getModifiers())) {
98				Class<?> type= field.getType();
99				if (sig.canAcceptArrayType(type)
100						&& field.getAnnotation(DataPoints.class) != null) {
101					addArrayValues(field.getName(), list, getStaticFieldValue(field));
102				} else if (sig.canAcceptType(type)
103						&& field.getAnnotation(DataPoint.class) != null) {
104					list.add(PotentialAssignment
105							.forValue(field.getName(), getStaticFieldValue(field)));
106				}
107			}
108		}
109	}
110
111	private void addArrayValues(String name, List<PotentialAssignment> list, Object array) {
112		for (int i= 0; i < Array.getLength(array); i++)
113			list.add(PotentialAssignment.forValue(name + "[" + i + "]", Array.get(array, i)));
114	}
115
116	private Object getStaticFieldValue(final Field field) {
117		try {
118			return field.get(null);
119		} catch (IllegalArgumentException e) {
120			throw new RuntimeException(
121					"unexpected: field from getClass doesn't exist on object");
122		} catch (IllegalAccessException e) {
123			throw new RuntimeException(
124					"unexpected: getFields returned an inaccessible field");
125		}
126	}
127}