1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.callbackparams.combine.reflect;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.IdentityHashMap;
27 import java.util.Iterator;
28 import java.util.LinkedHashSet;
29 import java.util.Map;
30 import java.util.Set;
31 import org.callbackparams.combine.CombineAllPossible2Combinations;
32 import org.callbackparams.combine.CombineStrategy;
33 import org.callbackparams.combine.ValuesCollection;
34 import org.callbackparams.support.ExceptionUtil;
35
36
37
38
39
40
41
42
43
44
45 public abstract class CallbackRecordsFactory {
46
47 private static CallbackRecordsFactory instance;
48
49
50
51
52
53
54
55
56 public static CallbackRecordsFactory getInstance() {
57 if (null == instance) {
58 String annotationAwareFactoryClassName =
59 "org.callbackparams.combine.annotation.AnnotationAwareCallbackRecordsFactory";
60 try {
61 instance = (CallbackRecordsFactory)
62 Class.forName(annotationAwareFactoryClassName)
63 .newInstance();
64
65
66 } catch (UnsupportedClassVersionError detectionOf_JDK1_4) {
67 instance = new CallbackRecordsFactory() {};
68
69
70 } catch (Exception shouldNeverHappen) {
71 throw ExceptionUtil.unchecked(shouldNeverHappen);
72 }
73 }
74 return instance;
75 }
76
77
78
79
80
81
82 protected Set
83 if (null == testClass) {
84 return new LinkedHashSet();
85 }
86 final Set factoryClasses =
87 findCallbackValuesFactoryClasses(testClass.getSuperclass());
88
89 final Class[] interfaces = testClass.getInterfaces();
90 for (int i = 0 ; i < interfaces.length ; ++i) {
91 factoryClasses.addAll(
92 findCallbackValuesFactoryClasses(interfaces[i]));
93 }
94
95 final Class[] nestedClasses = testClass.getDeclaredClasses();
96 for (int i = 0 ; i < nestedClasses.length ; ++i) {
97 if (isCallbackValuesFactoryClass(nestedClasses[i])) {
98 factoryClasses.add(nestedClasses[i]);
99 }
100 }
101 return factoryClasses;
102 }
103
104
105
106
107
108 protected boolean isCallbackValuesFactoryClass(Class factoryProspect) {
109 try {
110 Method factoryMethod = factoryProspect
111 .getDeclaredMethod("values", (Class[]) null);
112 return 0 < (factoryMethod.getModifiers() & Modifier.STATIC)
113 && factoryMethod.getReturnType().isArray();
114
115 } catch (NoSuchMethodException x) {
116 return false;
117 }
118 }
119
120
121
122
123 private static Boolean[] booleanValues() {
124 return new Boolean[] {Boolean.FALSE, Boolean.TRUE};
125 }
126
127 public static Method valuesMethodFor(Class valuesClass)
128 throws NoSuchMethodException {
129 boolean useBooleanValues =
130 boolean.class == valuesClass
131 || Boolean.class == valuesClass;
132 return (useBooleanValues ? CallbackRecordsFactory.class : valuesClass)
133 .getDeclaredMethod(
134 useBooleanValues ? "booleanValues" : "values",
135 (Class[])null);
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public Combined[] retrieveCombinedArray(Class valuesClass) {
152 try {
153 Method m = valuesMethodFor(valuesClass);
154 if (false == m.isAccessible()) {
155 m.setAccessible(true);
156 }
157 int nbrOfValues = Array.getLength(m.invoke(null, (Object[])null));
158 Combined[] combinedValues = new Combined[nbrOfValues];
159 for (int i = 0; i < combinedValues.length; ++i) {
160 combinedValues[i] = new Combined(m, i);
161 }
162 return combinedValues;
163
164 } catch (Exception x) {
165 throw ExceptionUtil.unchecked(x);
166 }
167 }
168
169
170
171
172
173
174
175
176 public CombineStrategy retrieveCombineStrategy(Class testClass) {
177 return new CombineAllPossible2Combinations();
178 }
179
180
181
182
183
184
185
186
187
188
189
190 public Collection collectCallbackRecordsReflectively(Class testClass) {
191 ValuesCollection vc = new ValuesCollection();
192
193 Map
194
195 addAsCombinedValues(vc, fieldValues);
196 Set
197 new HashSet(fieldValues.values());
198
199 for (Iterator i = findCallbackValuesFactoryClasses(testClass)
200 .iterator() ; i.hasNext() ;) {
201 final Class factoryClass = (Class) i.next();
202 if (false == consumedValuesFactoryClasses.contains(factoryClass)) {
203 vc.add(retrieveCombinedArray(factoryClass));
204 }
205 }
206 return retrieveCombineStrategy(testClass).combine(vc);
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220 protected Map
221 return new IdentityHashMap();
222 }
223
224
225
226
227
228
229
230
231
232
233 protected boolean isValueAlsoAvailableAsCallback(Field f) {
234 return boolean.class != f.getType() && Boolean.class != f.getType();
235 }
236
237 private void addAsCombinedValues(
238 ValuesCollection vc, Map
239 for (Iterator
240 iter.hasNext();) {
241 Map.Entry
242 final Field f = (Field) entry.getKey();
243 final Combined[] combinedArray =
244 retrieveCombinedArray((Class)entry.getValue());
245
246 for (int i = 0; i < combinedArray.length; ++i) {
247 combinedArray[i].toBeInjected(
248 f, isValueAlsoAvailableAsCallback(f));
249 }
250 vc.add(combinedArray);
251 }
252 }
253
254
255
256
257 public static Combined[] asCombinedArray(Object record) {
258 Combined[] combinedArray = new Combined[sizeOfRecord(record)];
259 Iterator iRecord = iterateRecordElements(record);
260 for (int i = 0; i < combinedArray.length; ++i) {
261 final Object nextRecordElement = iRecord.next();
262 combinedArray[i] = nextRecordElement instanceof Combined
263 ? (Combined)nextRecordElement
264 : new Combined(nextRecordElement);
265 }
266 return combinedArray;
267 }
268
269 private static Iterator iterateRecordElements(Object record) {
270 if (record instanceof Object[]) {
271 record = Arrays.asList((Object[])record);
272 }
273 return ((Collection)record).iterator();
274 }
275
276 private static int sizeOfRecord(Object record) {
277 if (record instanceof Object[]) {
278 return ((Object[])record).length;
279 } else {
280 return ((Collection)record).size();
281 }
282 }
283 }