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.util.Collection;
23 import java.util.IdentityHashMap;
24 import java.util.Map;
25 import org.callbackparams.support.ExceptionUtil;
26
27
28
29
30 public class Combined {
31
32 private static Class[] METHOD_HAS_NO_PARAMETERS = {};
33
34 private final Object coreValue;
35 private Method staticMethod;
36 private final int index;
37
38
39
40
41
42
43
44 private final int subIndex;
45
46
47
48
49 private boolean isCallback = true;
50 private Field valueField = null;
51
52 public Combined(Object coreValue) {
53 this(null, -1, -1, coreValue);
54 }
55
56 Combined(Method staticMethod, int index) {
57 this(staticMethod, index, -1);
58 }
59
60 Combined(Method staticMethod, int index, int subIndex) {
61 this(staticMethod, index, subIndex, null);
62 }
63
64 Combined(Method staticMethod, int index, int subIndex, Object originalValue) {
65 this.staticMethod = staticMethod;
66 this.index = index;
67 this.subIndex = subIndex;
68 this.coreValue = originalValue;
69
70 try {
71 if (null != staticMethod) {
72 staticMethod.setAccessible(true);
73 }
74 } catch (SecurityException ignoreAndContinueAnyway) {}
75 }
76
77 public static Combined resurrectFromOtherClassLoader(Object combined2reload) {
78 if (combined2reload instanceof Combined) {
79 return (Combined) combined2reload;
80
81 } else {
82 Combined resurrected = new Combined(
83 (Method) valueOfField("staticMethod", combined2reload),
84 ((Integer)valueOfField("index", combined2reload)).intValue(),
85 ((Integer)valueOfField("subIndex", combined2reload)).intValue(),
86 valueOfField("coreValue", combined2reload));
87 resurrected.toBeInjected(
88 (Field) valueOfField("valueField", combined2reload),
89 Boolean
90
91
92
93
94
95
96 .TRUE.equals(valueOfField("isCallback", combined2reload)));
97 return resurrected;
98 }
99 }
100
101 private static Object valueOfField(String fieldName, Object obj) {
102 Class c = obj.getClass();
103 try {
104 Field f = c.getDeclaredField(fieldName);
105 f.setAccessible(true);
106 return f.get(obj);
107 } catch (Exception ex) {
108 throw ExceptionUtil.unchecked(ex);
109 }
110 }
111
112 void toBeInjected(Field valueField, boolean alsoAvailableAsCallback) {
113 if (null != this.valueField) {
114 throw new IllegalStateException(
115 "Field for value-injection has already been set: " + this.valueField);
116 }
117 this.valueField = valueField;
118 this.isCallback = alsoAvailableAsCallback;
119
120 try {
121 if (null != valueField) {
122 this.valueField.setAccessible(true);
123 }
124 } catch (SecurityException ignoreAndContinueAnyway) {}
125 }
126
127 public Object getCoreValue() {
128 return null!=coreValue || null==staticMethod ? coreValue : getCoreValue(
129 staticMethod.getDeclaringClass().getClassLoader(),
130 new IdentityHashMap(1));
131 }
132
133 private Object getCoreValue(
134 ClassLoader cl, Map
135 if (null == this.staticMethod) {
136
137
138
139
140 return this.coreValue;
141 }
142
143
144
145
146
147 Method staticMethod = getStaticMethodOnClassLoader(cl);
148 if (false == valuesCache.containsValue(staticMethod)) {
149 try {
150 valuesCache.put(staticMethod, staticMethod
151 .invoke(null, (Object[])METHOD_HAS_NO_PARAMETERS));
152 } catch (Exception ex) {
153 throw ExceptionUtil.unchecked(ex);
154 }
155 }
156 Object valuesElement = Array.get(valuesCache.get(staticMethod), index);
157 if (subIndex < 0) {
158 return valuesElement;
159 } else if (valuesElement.getClass().isArray()) {
160 return Array.get(valuesElement, subIndex);
161 } else {
162 return ((Collection)valuesElement).toArray()[subIndex];
163 }
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 public Object retrieve(Object testInstance, Map
184 final ClassLoader cl = testInstance.getClass().getClassLoader();
185 final Object coreValue = getCoreValue(
186 testInstance.getClass().getClassLoader(), valuesCache);
187
188 if (null != valueField) {
189 try {
190 getFieldOnClassLoader(cl).set(testInstance, coreValue);
191 } catch (Exception ex) {
192 throw new Error("Unable to inject " + valueField
193 + " with core value: " + coreValue, ex);
194 }
195 }
196 return isCallback ? coreValue : null;
197 }
198
199 private Method getStaticMethodOnClassLoader(ClassLoader cl) {
200 Method staticMethod = this.staticMethod;
201 if (null == cl) {
202
203 return staticMethod;
204 }
205 try {
206 final Class declaringClass = cl
207 .loadClass(staticMethod.getDeclaringClass().getName());
208 if (declaringClass != staticMethod.getDeclaringClass()) {
209 staticMethod = declaringClass.getDeclaredMethod(
210 staticMethod.getName(), METHOD_HAS_NO_PARAMETERS);
211 staticMethod.setAccessible(true);
212
213
214
215
216 this.staticMethod = staticMethod;
217 }
218 } catch (SecurityException ignoreAndContinueAnyway) {
219 } catch (Exception ex) {
220 throw new Error("Cannot reach static method on test class-loader: "
221 + staticMethod, ex);
222 }
223 return staticMethod;
224 }
225
226 private Field getFieldOnClassLoader(ClassLoader cl) {
227 Field valueField = this.valueField;
228 try {
229 final Class declaringClass = cl
230 .loadClass(valueField.getDeclaringClass().getName());
231 if (declaringClass != valueField.getDeclaringClass()) {
232 valueField = declaringClass.getDeclaredField(valueField.getName());
233 valueField.setAccessible(true);
234
235
236
237
238 this.valueField = valueField;
239 }
240 } catch (SecurityException ignoreAndContinueAnyway) {
241 } catch (Exception ex) {
242 throw new Error(
243 "Cannot reach @ParameterizedValue-field on test class-loader: "
244 + valueField, ex);
245 }
246 return valueField;
247 }
248
249
250 public String toString() {
251 return (null == valueField ? "" : valueField.getName() + '=')
252 + getCoreValue();
253 }
254 }