1 package org.codehaus.plexus.util.reflection;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.HashMap;
24 import java.util.Map;
25
26
27
28
29
30
31
32 public final class Reflector {
33 private static final String CONSTRUCTOR_METHOD_NAME = "$$CONSTRUCTOR$$";
34
35 private static final String GET_INSTANCE_METHOD_NAME = "getInstance";
36
37 private Map<String, Map<String, Map<String, Method>>> classMaps =
38 new HashMap<String, Map<String, Map<String, Method>>>();
39
40
41 public Reflector() {}
42
43
44
45
46
47
48
49
50
51
52
53 @SuppressWarnings({"UnusedDeclaration"})
54 public <T> T newInstance(Class<T> theClass, Object[] params) throws ReflectorException {
55 if (params == null) {
56 params = new Object[0];
57 }
58
59 Class[] paramTypes = new Class[params.length];
60
61 for (int i = 0, len = params.length; i < len; i++) {
62 paramTypes[i] = params[i].getClass();
63 }
64
65 try {
66 Constructor<T> con = getConstructor(theClass, paramTypes);
67
68 if (con == null) {
69 StringBuilder buffer = new StringBuilder();
70
71 buffer.append("Constructor not found for class: ");
72 buffer.append(theClass.getName());
73 buffer.append(" with specified or ancestor parameter classes: ");
74
75 for (Class paramType : paramTypes) {
76 buffer.append(paramType.getName());
77 buffer.append(',');
78 }
79
80 buffer.setLength(buffer.length() - 1);
81
82 throw new ReflectorException(buffer.toString());
83 }
84
85 return con.newInstance(params);
86 } catch (InstantiationException | InvocationTargetException | IllegalAccessException ex) {
87 throw new ReflectorException(ex);
88 }
89 }
90
91
92
93
94
95
96
97
98
99
100
101 @SuppressWarnings({"UnusedDeclaration"})
102 public <T> T getSingleton(Class<T> theClass, Object[] initParams) throws ReflectorException {
103 Class[] paramTypes = new Class[initParams.length];
104
105 for (int i = 0, len = initParams.length; i < len; i++) {
106 paramTypes[i] = initParams[i].getClass();
107 }
108
109 try {
110 Method method = getMethod(theClass, GET_INSTANCE_METHOD_NAME, paramTypes);
111
112
113 return (T) method.invoke(null, initParams);
114 } catch (InvocationTargetException | IllegalAccessException ex) {
115 throw new ReflectorException(ex);
116 }
117 }
118
119
120
121
122
123
124
125
126
127
128 @SuppressWarnings({"UnusedDeclaration"})
129 public Object invoke(Object target, String methodName, Object[] params) throws ReflectorException {
130 if (params == null) {
131 params = new Object[0];
132 }
133
134 Class[] paramTypes = new Class[params.length];
135
136 for (int i = 0, len = params.length; i < len; i++) {
137 paramTypes[i] = params[i].getClass();
138 }
139
140 try {
141 Method method = getMethod(target.getClass(), methodName, paramTypes);
142
143 if (method == null) {
144 StringBuilder buffer = new StringBuilder();
145
146 buffer.append("Singleton-producing method named '")
147 .append(methodName)
148 .append("' not found with specified parameter classes: ");
149
150 for (Class paramType : paramTypes) {
151 buffer.append(paramType.getName());
152 buffer.append(',');
153 }
154
155 buffer.setLength(buffer.length() - 1);
156
157 throw new ReflectorException(buffer.toString());
158 }
159
160 return method.invoke(target, params);
161 } catch (InvocationTargetException | IllegalAccessException ex) {
162 throw new ReflectorException(ex);
163 }
164 }
165
166 @SuppressWarnings({"UnusedDeclaration"})
167 public Object getStaticField(Class targetClass, String fieldName) throws ReflectorException {
168 try {
169 Field field = targetClass.getField(fieldName);
170
171 return field.get(null);
172 } catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
173 throw new ReflectorException(e);
174 }
175 }
176
177 @SuppressWarnings({"UnusedDeclaration"})
178 public Object getField(Object target, String fieldName) throws ReflectorException {
179 return getField(target, fieldName, false);
180 }
181
182 public Object getField(Object target, String fieldName, boolean breakAccessibility) throws ReflectorException {
183 Class targetClass = target.getClass();
184 while (targetClass != null) {
185 try {
186 Field field = targetClass.getDeclaredField(fieldName);
187
188 boolean accessibilityBroken = false;
189 if (!field.isAccessible() && breakAccessibility) {
190 field.setAccessible(true);
191 accessibilityBroken = true;
192 }
193
194 Object result = field.get(target);
195
196 if (accessibilityBroken) {
197 field.setAccessible(false);
198 }
199
200 return result;
201 } catch (SecurityException e) {
202 throw new ReflectorException(e);
203 } catch (NoSuchFieldException e) {
204 if (targetClass == Object.class) throw new ReflectorException(e);
205 targetClass = targetClass.getSuperclass();
206 } catch (IllegalAccessException e) {
207 throw new ReflectorException(e);
208 }
209 }
210
211 return null;
212 }
213
214
215
216
217
218
219
220
221
222
223 @SuppressWarnings({"UnusedDeclaration"})
224 public Object invokeStatic(Class targetClass, String methodName, Object[] params) throws ReflectorException {
225 if (params == null) {
226 params = new Object[0];
227 }
228
229 Class[] paramTypes = new Class[params.length];
230
231 for (int i = 0, len = params.length; i < len; i++) {
232 paramTypes[i] = params[i].getClass();
233 }
234
235 try {
236 Method method = getMethod(targetClass, methodName, paramTypes);
237
238 if (method == null) {
239 StringBuilder buffer = new StringBuilder();
240
241 buffer.append("Singleton-producing method named \'")
242 .append(methodName)
243 .append("\' not found with specified parameter classes: ");
244
245 for (Class paramType : paramTypes) {
246 buffer.append(paramType.getName());
247 buffer.append(',');
248 }
249
250 buffer.setLength(buffer.length() - 1);
251
252 throw new ReflectorException(buffer.toString());
253 }
254
255 return method.invoke(null, params);
256 } catch (InvocationTargetException | IllegalAccessException ex) {
257 throw new ReflectorException(ex);
258 }
259 }
260
261
262
263
264
265
266
267
268
269
270 public <T> Constructor<T> getConstructor(Class<T> targetClass, Class[] params) throws ReflectorException {
271 Map<String, Constructor<T>> constructorMap = getConstructorMap(targetClass);
272
273 StringBuilder key = new StringBuilder(200);
274
275 key.append("(");
276
277 for (Class param : params) {
278 key.append(param.getName());
279 key.append(",");
280 }
281
282 if (params.length > 0) {
283 key.setLength(key.length() - 1);
284 }
285
286 key.append(")");
287
288 Constructor<T> constructor;
289
290 String paramKey = key.toString();
291
292 synchronized (paramKey.intern()) {
293 constructor = constructorMap.get(paramKey);
294
295 if (constructor == null) {
296 @SuppressWarnings({"unchecked"})
297 Constructor<T>[] cands = (Constructor<T>[]) targetClass.getConstructors();
298
299 for (Constructor<T> cand : cands) {
300 Class[] types = cand.getParameterTypes();
301
302 if (params.length != types.length) {
303 continue;
304 }
305
306 for (int j = 0, len2 = params.length; j < len2; j++) {
307 if (!types[j].isAssignableFrom(params[j])) {
308 continue;
309 }
310 }
311
312
313 constructor = cand;
314 constructorMap.put(paramKey, constructor);
315 }
316 }
317 }
318
319 if (constructor == null) {
320 throw new ReflectorException(
321 "Error retrieving constructor object for: " + targetClass.getName() + paramKey);
322 }
323
324 return constructor;
325 }
326
327 public Object getObjectProperty(Object target, String propertyName) throws ReflectorException {
328 Object returnValue;
329
330 if (propertyName == null || propertyName.trim().length() < 1) {
331 throw new ReflectorException("Cannot retrieve value for empty property.");
332 }
333
334 String beanAccessor = "get" + Character.toUpperCase(propertyName.charAt(0));
335 if (propertyName.trim().length() > 1) {
336 beanAccessor += propertyName.substring(1).trim();
337 }
338
339 Class targetClass = target.getClass();
340 Class[] emptyParams = {};
341
342 Method method = _getMethod(targetClass, beanAccessor, emptyParams);
343 if (method == null) {
344 method = _getMethod(targetClass, propertyName, emptyParams);
345 }
346 if (method != null) {
347 try {
348 returnValue = method.invoke(target, new Object[] {});
349 } catch (IllegalAccessException e) {
350 throw new ReflectorException(
351 "Error retrieving property \'" + propertyName + "\' from \'" + targetClass + "\'", e);
352 } catch (InvocationTargetException e) {
353 throw new ReflectorException(
354 "Error retrieving property \'" + propertyName + "\' from \'" + targetClass + "\'", e);
355 }
356 }
357
358 if (method != null) {
359 try {
360 returnValue = method.invoke(target, new Object[] {});
361 } catch (IllegalAccessException e) {
362 throw new ReflectorException(
363 "Error retrieving property \'" + propertyName + "\' from \'" + targetClass + "\'", e);
364 } catch (InvocationTargetException e) {
365 throw new ReflectorException(
366 "Error retrieving property \'" + propertyName + "\' from \'" + targetClass + "\'", e);
367 }
368 } else {
369 returnValue = getField(target, propertyName, true);
370 if (returnValue == null) {
371
372 throw new ReflectorException("Neither method: \'" + propertyName + "\' nor bean accessor: \'"
373 + beanAccessor + "\' can be found for class: \'" + targetClass
374 + "\', and retrieval of field: \'"
375 + propertyName + "\' returned null as value.");
376 }
377 }
378
379 return returnValue;
380 }
381
382
383
384
385
386
387
388
389
390
391 public Method getMethod(Class targetClass, String methodName, Class[] params) throws ReflectorException {
392 Method method = _getMethod(targetClass, methodName, params);
393
394 if (method == null) {
395 throw new ReflectorException("Method: \'" + methodName + "\' not found in class: \'" + targetClass + "\'");
396 }
397
398 return method;
399 }
400
401 private Method _getMethod(Class targetClass, String methodName, Class[] params) throws ReflectorException {
402 Map<String, Method> methodMap = (Map<String, Method>) getMethodMap(targetClass, methodName);
403
404 StringBuilder key = new StringBuilder(200);
405
406 key.append("(");
407
408 for (Class param : params) {
409 key.append(param.getName());
410 key.append(",");
411 }
412
413 key.append(")");
414
415 Method method;
416
417 String paramKey = key.toString();
418
419 synchronized (paramKey.intern()) {
420 method = methodMap.get(paramKey);
421
422 if (method == null) {
423 Method[] cands = targetClass.getMethods();
424
425 for (Method cand : cands) {
426 String name = cand.getName();
427
428 if (!methodName.equals(name)) {
429 continue;
430 }
431
432 Class[] types = cand.getParameterTypes();
433
434 if (params.length != types.length) {
435 continue;
436 }
437
438 for (int j = 0, len2 = params.length; j < len2; j++) {
439 if (!types[j].isAssignableFrom(params[j])) {
440 continue;
441 }
442 }
443
444
445 method = cand;
446 methodMap.put(paramKey, method);
447 }
448 }
449 }
450
451 return method;
452 }
453
454
455
456
457
458
459
460
461 private <T> Map<String, Constructor<T>> getConstructorMap(Class<T> theClass) throws ReflectorException {
462 return (Map<String, Constructor<T>>) getMethodMap(theClass, CONSTRUCTOR_METHOD_NAME);
463 }
464
465
466
467
468
469
470
471
472
473 private Map<String, ?> getMethodMap(Class theClass, String methodName) throws ReflectorException {
474 Map<String, Method> methodMap;
475
476 if (theClass == null) {
477 return null;
478 }
479
480 String className = theClass.getName();
481
482 synchronized (className.intern()) {
483 Map<String, Map<String, Method>> classMethods = classMaps.get(className);
484
485 if (classMethods == null) {
486 classMethods = new HashMap<>();
487 methodMap = new HashMap<>();
488 classMethods.put(methodName, methodMap);
489 classMaps.put(className, classMethods);
490 } else {
491 String key = className + "::" + methodName;
492
493 synchronized (key.intern()) {
494 methodMap = classMethods.get(methodName);
495
496 if (methodMap == null) {
497 methodMap = new HashMap<>();
498 classMethods.put(methodName, methodMap);
499 }
500 }
501 }
502 }
503
504 return methodMap;
505 }
506 }