1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * TheseFoolishThings: Miscellaneous utilities
5 * http://tidalwave.it/projects/thesefoolishthings
6 *
7 * Copyright (C) 2009 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22 * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.messagebus.spi;
27
28 import java.lang.annotation.Annotation;
29 import java.lang.reflect.Method;
30 import javax.annotation.Nonnull;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34
35 /***************************************************************************************************************************************************************
36 *
37 * @author Fabrizio Giudici
38 *
39 **************************************************************************************************************************************************************/
40 public class ReflectionUtils
41 {
42 /***********************************************************************************************************************************************************
43 *
44 **********************************************************************************************************************************************************/
45 public static interface MethodProcessor
46 {
47 enum FilterResult { ACCEPT, IGNORE }
48
49 public FilterResult filter (@Nonnull Class<?> clazz);
50
51 public void process (@Nonnull Method method);
52 }
53
54 /***********************************************************************************************************************************************************
55 *
56 **********************************************************************************************************************************************************/
57 public static class MethodProcessorSupport implements MethodProcessor
58 {
59 @Override @Nonnull
60 public FilterResult filter (@Nonnull final Class<?> clazz)
61 {
62 return FilterResult.ACCEPT;
63 }
64
65 @Override
66 public void process (@Nonnull final Method method)
67 {
68 }
69 }
70
71 /***********************************************************************************************************************************************************
72 * Navigates the hierarchy of the given object, top down, and applies the {@link MethodProcessor} to all the
73 * methods of each class, if not filtered out by the processor itself.
74 *
75 * @param object the object at the bottom of the hierarchy
76 * @param processor the processor
77 **********************************************************************************************************************************************************/
78 public static void forEachMethodInTopDownHierarchy (@Nonnull final Object object,
79 @Nonnull final MethodProcessor processor)
80 {
81 for (final var clazz : getClassHierarchy(object.getClass()))
82 {
83 for (final var method : clazz.getDeclaredMethods())
84 {
85 processor.process(method);
86 }
87 }
88 }
89
90 /***********************************************************************************************************************************************************
91 * Navigates the hierarchy of the given object, bottom up, and applies the {@link MethodProcessor} to all the
92 * methods of each class, if not filtered out by the processor itself.
93 *
94 * @param object the object at the bottom of the hierarchy
95 * @param processor the processor
96 **********************************************************************************************************************************************************/
97 public static void forEachMethodInBottomUpHierarchy (@Nonnull final Object object,
98 @Nonnull final MethodProcessor processor)
99 {
100 final var hierarchy = getClassHierarchy(object.getClass());
101 Collections.reverse(hierarchy);
102
103 for (final var clazz : hierarchy)
104 {
105 if (processor.filter(clazz) == MethodProcessor.FilterResult.ACCEPT)
106 {
107 for (final var method : clazz.getDeclaredMethods())
108 {
109 processor.process(method);
110 }
111 }
112 }
113 }
114
115 /***********************************************************************************************************************************************************
116 * Returns the hierarchy of the given class, top down.
117 *
118 * @param clazz the clazz at the bottom of the hierarchy
119 * @return the hierarchy
120 **********************************************************************************************************************************************************/
121 @Nonnull
122 private static List<Class<?>> getClassHierarchy (@Nonnull final Class<?> clazz)
123 {
124 final List<Class<?>> hierarchy = new ArrayList<>();
125
126 for (var ancestor = clazz; ancestor != null; ancestor = ancestor.getSuperclass())
127 {
128 hierarchy.add(0, ancestor);
129 }
130
131 return hierarchy;
132 }
133
134 /***********************************************************************************************************************************************************
135 * Checks whether an array of annotations contains an annotation of the given type.
136 *
137 * @param annotations the annotations to check
138 * @param annotationClass the type of the required annotation
139 * @return true if the annotation is found
140 **********************************************************************************************************************************************************/
141 public static boolean containsAnnotation (@Nonnull final Annotation[] annotations,
142 @Nonnull final Class<?> annotationClass)
143 {
144 for (final var annotation : annotations)
145 {
146 if (annotationClass.isAssignableFrom(annotation.getClass()))
147 {
148 return true;
149 }
150 }
151
152 return false;
153 }
154 }