43 | * These exceptions are 44 | *
54 | * An example of using
78 | * This is the same as calling
92 | * This is the same as calling
93 | *
109 | * Use this when you want to access static fields and methods on a
110 | * {@link Class} object, or as a basis for constructing objects of that
111 | * class using {@link #create(Object...)}
112 | *
113 | * @param clazz The class to be wrapped
114 | * @return A wrapped class object, to be used for further reflection.
115 | */
116 | public static Reflect on(Class> clazz) {
117 | return new Reflect(clazz);
118 | }
119 |
120 | /**
121 | * Wrap an object.
122 | *
123 | * Use this when you want to access instance fields and methods on any
124 | * {@link Object}
125 | *
126 | * @param object The object to be wrapped
127 | * @return A wrapped object, to be used for further reflection.
128 | */
129 | public static Reflect on(Object object) {
130 | return new Reflect(object);
131 | }
132 |
133 | /**
134 | * Conveniently render an {@link AccessibleObject} accessible.
135 | *
136 | * To prevent {@link SecurityException}, this is only done if the argument
137 | * object and its declaring class are non-public.
138 | *
139 | * @param accessible The object to render accessible
140 | * @return The argument object rendered accessible
141 | */
142 | public static
212 | * This is roughly equivalent to {@link Field#set(Object, Object)}. If the
213 | * wrapped object is a {@link Class}, then this will set a value to a static
214 | * member field. If the wrapped object is any other {@link Object}, then
215 | * this will set a value to an instance member field.
216 | *
217 | * @param name The field name
218 | * @param value The new field value
219 | * @return The same wrapped object, to be used for further reflection.
220 | * @throws ReflectException If any reflection exception occurred.
221 | */
222 | public Reflect set(String name, Object value) throws ReflectException {
223 | try {
224 | Field field = field0(name);
225 | field.set(object, unwrap(value));
226 | return this;
227 | }
228 | catch (Exception e) {
229 | throw new ReflectException(e);
230 | }
231 | }
232 |
233 | /**
234 | * Get a field value.
235 | *
236 | * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped
237 | * object is a {@link Class}, then this will get a value from a static
238 | * member field. If the wrapped object is any other {@link Object}, then
239 | * this will get a value from an instance member field.
240 | *
241 | * If you want to "navigate" to a wrapped version of the field, use
242 | * {@link #field(String)} instead.
243 | *
244 | * @param name The field name
245 | * @return The field value
246 | * @throws ReflectException If any reflection exception occurred.
247 | * @see #field(String)
248 | */
249 | public
256 | * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped
257 | * object is a {@link Class}, then this will wrap a static member field. If
258 | * the wrapped object is any other {@link Object}, then this wrap an
259 | * instance member field.
260 | *
261 | * @param name The field name
262 | * @return The wrapped field
263 | * @throws ReflectException If any reflection exception occurred.
264 | */
265 | public Reflect field(String name) throws ReflectException {
266 | try {
267 | Field field = field0(name);
268 | return on(field.get(object));
269 | }
270 | catch (Exception e) {
271 | throw new ReflectException(e);
272 | }
273 | }
274 |
275 | private Field field0(String name) throws ReflectException {
276 | Class> type = type();
277 |
278 | // Try getting a public field
279 | try {
280 | return type.getField(name);
281 | }
282 |
283 | // Try again, getting a non-public field
284 | catch (NoSuchFieldException e) {
285 | do {
286 | try {
287 | return accessible(type.getDeclaredField(name));
288 | }
289 | catch (NoSuchFieldException ignore) {}
290 |
291 | type = type.getSuperclass();
292 | }
293 | while (type != null);
294 |
295 | throw new ReflectException(e);
296 | }
297 | }
298 |
299 | /**
300 | * Get a Map containing field names and wrapped values for the fields'
301 | * values.
302 | *
303 | * If the wrapped object is a {@link Class}, then this will return static
304 | * fields. If the wrapped object is any other {@link Object}, then this will
305 | * return instance fields.
306 | *
307 | * These two calls are equivalent
338 | * This is a convenience method for calling
339 | *
355 | * This is roughly equivalent to {@link Method#invoke(Object, Object...)}.
356 | * If the wrapped object is a {@link Class}, then this will invoke a static
357 | * method. If the wrapped object is any other {@link Object}, then this will
358 | * invoke an instance method.
359 | *
360 | * Just like {@link Method#invoke(Object, Object...)}, this will try to wrap
361 | * primitive types or unwrap primitive type wrappers if applicable. If
362 | * several methods are applicable, by that rule, the first one encountered
363 | * is called. i.e. when calling
374 | * The best matching method is searched for with the following strategy:
375 | *
414 | * If a public method is found in the class hierarchy, this method is returned.
415 | * Otherwise a private method with the exact same signature is returned.
416 | * If no exact match could be found, we let the {@code NoSuchMethodException} pass through.
417 | */
418 | private Method exactMethod(String name, Class>[] types) throws NoSuchMethodException {
419 | Class> type = type();
420 |
421 | // first priority: find a public method with exact signature match in class hierarchy
422 | try {
423 | return type.getMethod(name, types);
424 | }
425 |
426 | // second priority: find a private method with exact signature match on declaring class
427 | catch (NoSuchMethodException e) {
428 | do {
429 | try {
430 | return type.getDeclaredMethod(name, types);
431 | }
432 | catch (NoSuchMethodException ignore) {}
433 |
434 | type = type.getSuperclass();
435 | }
436 | while (type != null);
437 |
438 | throw new NoSuchMethodException();
439 | }
440 | }
441 |
442 | /**
443 | * Searches a method with a similar signature as desired using
444 | * {@link #isSimilarSignature(Method, String, Class[])}.
445 | *
446 | * First public methods are searched in the class hierarchy, then private
447 | * methods on the declaring class. If a method could be found, it is
448 | * returned, otherwise a {@code NoSuchMethodException} is thrown.
449 | */
450 | private Method similarMethod(String name, Class>[] types) throws NoSuchMethodException {
451 | Class> type = type();
452 |
453 | // first priority: find a public method with a "similar" signature in class hierarchy
454 | // similar interpreted in when primitive argument types are converted to their wrappers
455 | for (Method method : type.getMethods()) {
456 | if (isSimilarSignature(method, name, types)) {
457 | return method;
458 | }
459 | }
460 |
461 | // second priority: find a non-public method with a "similar" signature on declaring class
462 | do {
463 | for (Method method : type.getDeclaredMethods()) {
464 | if (isSimilarSignature(method, name, types)) {
465 | return method;
466 | }
467 | }
468 |
469 | type = type.getSuperclass();
470 | }
471 | while (type != null);
472 |
473 | throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + ".");
474 | }
475 |
476 | /**
477 | * Determines if a method has a "similar" signature, especially if wrapping
478 | * primitive argument types would result in an exactly matching signature.
479 | */
480 | private boolean isSimilarSignature(Method possiblyMatchingMethod, String desiredMethodName, Class>[] desiredParamTypes) {
481 | return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes);
482 | }
483 |
484 | /**
485 | * Call a constructor.
486 | *
487 | * This is a convenience method for calling
488 | *
501 | * This is roughly equivalent to {@link Constructor#newInstance(Object...)}.
502 | * If the wrapped object is a {@link Class}, then this will create a new
503 | * object of that class. If the wrapped object is any other {@link Object},
504 | * then this will create a new object of the same type.
505 | *
506 | * Just like {@link Constructor#newInstance(Object...)}, this will try to
507 | * wrap primitive types or unwrap primitive type wrappers if applicable. If
508 | * several constructors are applicable, by that rule, the first one
509 | * encountered is called. i.e. when calling P as(Class proxyType) {
556 | final boolean isMap = (object instanceof Map);
557 | final InvocationHandler handler = new InvocationHandler() {
558 | @SuppressWarnings("null")
559 | @Override
560 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
561 | String name = method.getName();
562 |
563 | // Actual method name matches always come first
564 | try {
565 | return on(object).call(name, args).get();
566 | }
567 |
568 | // [#14] Emulate POJO behaviour on wrapped map objects
569 | catch (ReflectException e) {
570 | if (isMap) {
571 | MapReflect is
55 | * // Static import all reflection methods to decrease verbosity
56 | * import static org.joor.Reflect.*;
57 | *
58 | * // Wrap an Object / Class / class name with the on() method:
59 | * on("java.lang.String")
60 | * // Invoke constructors using the create() method:
61 | * .create("Hello World")
62 | * // Invoke methods using the call() method:
63 | * .call("toString")
64 | * // Retrieve the wrapped object
65 | *
66 | * @author Lukas Eder
67 | * @author Irek Matysiewicz
68 | */
69 | public class Reflect {
70 |
71 | // ---------------------------------------------------------------------
72 | // Static API used as entrance points to the fluent API
73 | // ---------------------------------------------------------------------
74 |
75 | /**
76 | * Wrap a class name.
77 | * on(Class.forName(name))
79 | *
80 | * @param name A fully qualified class name
81 | * @return A wrapped class object, to be used for further reflection.
82 | * @throws ReflectException If any reflection exception occurred.
83 | * @see #on(Class)
84 | */
85 | public static Reflect on(String name) throws ReflectException {
86 | return on(forName(name));
87 | }
88 |
89 | /**
90 | * Wrap a class name, loading it via a given class loader.
91 | * on(Class.forName(name, classLoader))
94 | *
95 | * @param name A fully qualified class name.
96 | * @param classLoader The class loader in whose context the class should be
97 | * loaded.
98 | * @return A wrapped class object, to be used for further reflection.
99 | * @throws ReflectException If any reflection exception occurred.
100 | * @see #on(Class)
101 | */
102 | public static Reflect on(String name, ClassLoader classLoader) throws ReflectException {
103 | return on(forName(name, classLoader));
104 | }
105 |
106 | /**
107 | * Wrap a class.
108 | *
311 | *
312 | * @return A map containing field names and wrapped values.
313 | */
314 | public Map
308 | * on(object).field("myField");
309 | * on(object).fields().get("myField");
310 | * call(name, new Object[0])
340 | *
341 | * @param name The method name
342 | * @return The wrapped method result or the same wrapped object if the
343 | * method returns void, to be used for further
344 | * reflection.
345 | * @throws ReflectException If any reflection exception occurred.
346 | * @see #call(String, Object...)
347 | */
348 | public Reflect call(String name) throws ReflectException {
349 | return call(name, new Object[0]);
350 | }
351 |
352 | /**
353 | * Call a method by its name.
354 | * The first of the following methods will be called:
366 | *
364 | * on(...).call("method", 1, 1);
365 | *
373 | *
367 | * public void method(int param1, Integer param2);
368 | * public void method(Integer param1, int param2);
369 | * public void method(Number param1, Number param2);
370 | * public void method(Number param1, Object param2);
371 | * public void method(int param1, Object param2);
372 | *
376 | *
381 | *
382 | * @param name The method name
383 | * @param args The method arguments
384 | * @return The wrapped method result or the same wrapped object if the
385 | * method returns void, to be used for further
386 | * reflection.
387 | * @throws ReflectException If any reflection exception occurred.
388 | */
389 | public Reflect call(String name, Object... args) throws ReflectException {
390 | Class>[] types = types(args);
391 |
392 | // Try invoking the "canonical" method, i.e. the one with exact
393 | // matching argument types
394 | try {
395 | Method method = exactMethod(name, types);
396 | return on(method, object, args);
397 | }
398 |
399 | // If there is no exact match, try to find a method that has a "similar"
400 | // signature if primitive argument types are converted to their wrappers
401 | catch (NoSuchMethodException e) {
402 | try {
403 | Method method = similarMethod(name, types);
404 | return on(method, object, args);
405 | } catch (NoSuchMethodException e1) {
406 | throw new ReflectException(e1);
407 | }
408 | }
409 | }
410 |
411 | /**
412 | * Searches a method with the exact same signature as desired.
413 | * create(new Object[0])
489 | *
490 | * @return The wrapped new object, to be used for further reflection.
491 | * @throws ReflectException If any reflection exception occurred.
492 | * @see #create(Object...)
493 | */
494 | public Reflect create() throws ReflectException {
495 | return create(new Object[0]);
496 | }
497 |
498 | /**
499 | * Call a constructor.
500 | * The first of the following constructors will be applied:
512 | *
510 | * on(C.class).create(1, 1);
511 | *
519 | *
520 | * @param args The constructor arguments
521 | * @return The wrapped new object, to be used for further reflection.
522 | * @throws ReflectException If any reflection exception occurred.
523 | */
524 | public Reflect create(Object... args) throws ReflectException {
525 | Class>[] types = types(args);
526 |
527 | // Try invoking the "canonical" constructor, i.e. the one with exact
528 | // matching argument types
529 | try {
530 | Constructor> constructor = type().getDeclaredConstructor(types);
531 | return on(constructor, args);
532 | }
533 |
534 | // If there is no exact match, try to find one that has a "similar"
535 | // signature if primitive argument types are converted to their wrappers
536 | catch (NoSuchMethodException e) {
537 | for (Constructor> constructor : type().getDeclaredConstructors()) {
538 | if (match(constructor.getParameterTypes(), types)) {
539 | return on(constructor, args);
540 | }
541 | }
542 |
543 | throw new ReflectException(e);
544 | }
545 | }
546 |
547 | /**
548 | * Create a proxy for the wrapped object allowing to typesafely invoke
549 | * methods on it using a custom interface
550 | *
551 | * @param proxyType The interface type that is implemented by the proxy
552 | * @return A proxy for the wrapped object
553 | */
554 | @SuppressWarnings("unchecked")
555 | public
513 | * public C(int param1, Integer param2);
514 | * public C(Integer param1, int param2);
515 | * public C(Number param1, Number param2);
516 | * public C(Number param1, Object param2);
517 | * public C(int param1, Object param2);
518 | *