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 | Map
43 | * These exceptions are
44 | * Reflect
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 | *
45 | *
54 | *
55 | * @author Lukas Eder
56 | */
57 | public class ReflectException extends RuntimeException {
58 |
59 | /**
60 | * Generated UID
61 | */
62 | private static final long serialVersionUID = -6213149635297151442L;
63 |
64 | public ReflectException(String message) {
65 | super(message);
66 | }
67 |
68 | public ReflectException(String message, Throwable cause) {
69 | super(message, cause);
70 | }
71 |
72 | public ReflectException() {
73 | super();
74 | }
75 |
76 | public ReflectException(Throwable cause) {
77 | super(cause);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/lzh/courier/util/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.lzh.courier.util;
2 |
3 |
4 | public class StringUtils {
5 |
6 | public static boolean isEmpty(CharSequence data) {
7 | if (data == null || data.length() == 0) {
8 | return true;
9 | }
10 | return false;
11 | }
12 |
13 | public static String getSetMethodName(String fieldName) {
14 | checkName(fieldName);
15 |
16 | StringBuffer sb = getUpperMethod(fieldName);
17 | return "set" + sb.toString();
18 | }
19 |
20 | private static StringBuffer getUpperMethod(String fieldName) {
21 | char[] charArray = fieldName.toCharArray();
22 | StringBuffer sb = new StringBuffer();
23 | for (int i = 0; i < charArray.length; i++) {
24 | if (i == 0) {
25 | sb.append(Character.toUpperCase(charArray[i]));
26 | } else {
27 | sb.append(charArray[i]);
28 | }
29 | }
30 | return sb;
31 | }
32 |
33 | public static String getGetMethodName(String fieldName) {
34 | checkName(fieldName);
35 |
36 | StringBuffer sb = getUpperMethod(fieldName);
37 | return "get" + sb.toString();
38 | }
39 |
40 | private static void checkName(String fieldName) {
41 | if (isEmpty(fieldName)) {
42 | throw new IllegalArgumentException("method name is null");
43 | }
44 | }
45 |
46 | public static String getIsMethodName(String fieldName) {
47 | checkName(fieldName);
48 |
49 | StringBuffer sb = getUpperMethod(fieldName);
50 | return "is" + sb.toString();
51 | }
52 |
53 | public static String spliteFieldName(String methodName) {
54 | if (isEmpty(methodName)) {
55 | return methodName;
56 | }
57 |
58 | if (methodName.startsWith("is")) {
59 | methodName = methodName.replaceFirst("is", "");
60 | } else if (methodName.startsWith("set")) {
61 | methodName = methodName.replaceFirst("set", "");
62 | } else if (methodName.startsWith("get")) {
63 | methodName = methodName.replaceFirst("get", "");
64 | }
65 |
66 | return getUpperMethod(methodName).toString();
67 |
68 | }
69 |
70 | public static String getAddMethodName(String name) {
71 | checkName(name);
72 |
73 | StringBuffer sb = getUpperMethod(name);
74 | return "add" + sb.toString();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/lzh/courier/util/UtilMgr.java:
--------------------------------------------------------------------------------
1 | package com.lzh.courier.util;
2 |
3 | import javax.annotation.processing.Filer;
4 | import javax.annotation.processing.Messager;
5 | import javax.lang.model.util.Elements;
6 | import javax.lang.model.util.Types;
7 |
8 | /**
9 | * 单例。存放处理器相关工具。方便在各处任意调用
10 | */
11 | public class UtilMgr {
12 |
13 | private Elements elementUtils = null;
14 | private Filer filer = null;
15 | private Messager messager = null;
16 | private Types typeUtils = null;
17 |
18 | private static UtilMgr mgr = new UtilMgr();
19 |
20 | private UtilMgr() {
21 | }
22 |
23 | public static UtilMgr getMgr() {
24 | return mgr;
25 | }
26 |
27 | public Elements getElementUtils() {
28 | return elementUtils;
29 | }
30 |
31 | public void setElementUtils(Elements elementUtils) {
32 | this.elementUtils = elementUtils;
33 | }
34 |
35 | public Filer getFiler() {
36 | return filer;
37 | }
38 |
39 | public void setFiler(Filer filer) {
40 | this.filer = filer;
41 | }
42 |
43 | public Messager getMessager() {
44 | return messager;
45 | }
46 |
47 | public void setMessager(Messager messager) {
48 | this.messager = messager;
49 | }
50 |
51 | public Types getTypeUtils() {
52 | return typeUtils;
53 | }
54 |
55 | public void setTypeUtils(Types typeUtils) {
56 | this.typeUtils = typeUtils;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | com.lzh.courier.compiler.Compiler
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':api', ':processor'
2 |
--------------------------------------------------------------------------------