1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package eu.ehri.project.models.utils;
21
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Maps;
25 import com.google.common.collect.Sets;
26 import com.tinkerpop.blueprints.Direction;
27 import com.tinkerpop.frames.Adjacency;
28 import com.tinkerpop.frames.Property;
29 import eu.ehri.project.models.EntityClass;
30 import eu.ehri.project.models.annotations.Dependent;
31 import eu.ehri.project.models.annotations.EntityType;
32 import eu.ehri.project.models.annotations.Fetch;
33 import eu.ehri.project.models.annotations.Indexed;
34 import eu.ehri.project.models.annotations.Mandatory;
35 import eu.ehri.project.models.annotations.Meta;
36 import eu.ehri.project.models.annotations.Unique;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import java.lang.annotation.Annotation;
41 import java.lang.reflect.Method;
42 import java.util.Collection;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46
47
48
49
50 public class ClassUtils {
51
52 public static final String FETCH_METHOD_PREFIX = "get";
53
54 private static final Logger logger = LoggerFactory.getLogger(ClassUtils.class);
55
56 private static final Map<Class<?>,Map<String,Method>> fetchMethodCache = Maps.newHashMap();
57 private static final Map<Class<?>,Map<String,Method>> metaMethodCache = Maps.newHashMap();
58 private static final Map<Class<?>,Map<String,Set<String>>> enumPropertyValuesCache = Maps.newHashMap();
59 private static final Map<Class<?>,Collection<String>> propertyKeysCache = Maps.newHashMap();
60 private static final Map<Class<?>,Collection<String>> mandatoryPropertyKeysCache = Maps.newHashMap();
61 private static final Map<Class<?>,Collection<String>> indexedPropertyKeysCache = Maps.newHashMap();
62 private static final Map<Class<?>,Collection<String>> uniquePropertyKeysCache = Maps.newHashMap();
63 private static final Map<Class<?>,Map<String, Direction>> dependentRelationsCache = Maps.newHashMap();
64 private static final Map<Class<?>,EntityClass> entityClassCache = Maps.newHashMap();
65
66
67
68
69
70
71
72 public static EntityClass getEntityType(Class<?> cls) {
73 if (!entityClassCache.containsKey(cls)) {
74 entityClassCache.put(cls, getEntityTypeInternal(cls));
75 }
76 return entityClassCache.get(cls);
77 }
78
79
80
81
82
83
84
85
86 public static Map<String,Direction> getDependentRelations(Class<?> cls) {
87 if (!dependentRelationsCache.containsKey(cls)) {
88 dependentRelationsCache.put(cls, getDependentRelationsInternal(cls));
89 }
90 return dependentRelationsCache.get(cls);
91 }
92
93
94
95
96
97
98
99
100 public static Map<String, Method> getFetchMethods(Class<?> cls) {
101 if (!fetchMethodCache.containsKey(cls)) {
102 fetchMethodCache.put(cls, getFetchMethodsInternal(cls));
103 }
104 return fetchMethodCache.get(cls);
105 }
106
107
108
109
110
111
112
113
114 public static Map<String, Method> getMetaMethods(Class<?> cls) {
115 if (!metaMethodCache.containsKey(cls)) {
116 metaMethodCache.put(cls, getMetaMethodsInternal(cls));
117 }
118 return metaMethodCache.get(cls);
119 }
120
121
122
123
124
125
126
127 public static Collection<String> getPropertyKeys(Class<?> cls) {
128 if (!propertyKeysCache.containsKey(cls)) {
129 propertyKeysCache.put(cls, getAnnotatedPropertyKeys(cls, Property.class, true));
130 }
131 return propertyKeysCache.get(cls);
132 }
133
134
135
136
137
138
139
140 public static Collection<String> getMandatoryPropertyKeys(Class<?> cls) {
141 if (!mandatoryPropertyKeysCache.containsKey(cls)) {
142 mandatoryPropertyKeysCache.put(cls, getAnnotatedPropertyKeys(cls, Mandatory.class, false));
143 }
144 return mandatoryPropertyKeysCache.get(cls);
145 }
146
147
148
149
150
151
152
153 public static Collection<String> getIndexedPropertyKeys(Class<?> cls) {
154 if (!indexedPropertyKeysCache.containsKey(cls)) {
155 indexedPropertyKeysCache.put(cls, getAnnotatedPropertyKeys(cls, Indexed.class, false));
156 }
157 return indexedPropertyKeysCache.get(cls);
158 }
159
160
161
162
163
164
165
166
167 public static Collection<String> getUniquePropertyKeys(Class<?> cls) {
168 if (!uniquePropertyKeysCache.containsKey(cls)) {
169 uniquePropertyKeysCache.put(cls, getAnnotatedPropertyKeys(cls, Unique.class, true));
170 }
171 return uniquePropertyKeysCache.get(cls);
172 }
173
174
175
176
177
178
179
180
181 public static Map<String,Set<String>> getEnumPropertyKeys(Class<?> cls) {
182 if (!enumPropertyValuesCache.containsKey(cls)) {
183 enumPropertyValuesCache.put(cls, getEnumPropertyKeysInternal(cls));
184 }
185 return enumPropertyValuesCache.get(cls);
186 }
187
188 private static Map<String, Set<String>> getEnumPropertyKeysInternal(Class<?> cls) {
189 Map<String, Set<String>> out = Maps.newHashMap();
190 for (Method method : cls.getMethods()) {
191 Property ann = method.getAnnotation(Property.class);
192 if (ann != null) {
193 String name = ann.value();
194 Class<?> returnType = method.getReturnType();
195 if (Enum.class.isAssignableFrom(returnType)) {
196 Object[] values = returnType.getEnumConstants();
197 Set<String> strings = Sets.newHashSet();
198 for (Object v : values) {
199 strings.add(v.toString());
200 }
201 out.put(name, strings);
202 }
203 }
204 }
205 return out;
206 }
207
208 private static EntityClass getEntityTypeInternal(Class<?> cls) {
209 EntityType ann = cls.getAnnotation(EntityType.class);
210 if (ann == null)
211 throw new RuntimeException(String.format(
212 "Programming error! Bad bundle type: %s", cls.getName()));
213 return ann.value();
214 }
215
216 private static Map<String, Direction> getDependentRelationsInternal(Class<?> cls) {
217 Map<String, Direction> out = Maps.newHashMap();
218 for (Method method : cls.getMethods()) {
219 if (method.getAnnotation(Dependent.class) != null) {
220 Adjacency ann = method.getAnnotation(Adjacency.class);
221 if (ann != null)
222 out.put(ann.label(), ann.direction());
223 }
224 }
225 return out;
226 }
227
228 private static Map<String, Method> getFetchMethodsInternal(Class<?> cls) {
229 logger.trace(" - checking for @Fetch methods: {}", cls.getCanonicalName());
230 Map<String, Method> out = Maps.newHashMap();
231 for (Method method : cls.getMethods()) {
232 Fetch fetch = method.getAnnotation(Fetch.class);
233 Dependent dep = method.getAnnotation(Dependent.class);
234 String value = fetch != null ? fetch.value() : null;
235 if ((value != null || dep != null)
236 && method.getName().startsWith(FETCH_METHOD_PREFIX)) {
237 out.put(value, method);
238 logger.trace(" --- found @Fetch annotation: {}: {}", method.getName(), value);
239 }
240 }
241
242 for (Class<?> s : cls.getInterfaces()) {
243 out.putAll(getFetchMethodsInternal(s));
244 }
245 return out;
246 }
247
248 private static Map<String, Method> getMetaMethodsInternal(Class<?> cls) {
249 logger.trace(" - checking for @Meta methods: {}", cls.getCanonicalName());
250 Map<String, Method> out = Maps.newHashMap();
251 for (Method method : cls.getMethods()) {
252 Meta meta = method.getAnnotation(Meta.class);
253 String value = meta != null ? meta.value() : null;
254 if (value != null) {
255 out.put(value, method);
256 logger.trace(" --- found @Meta annotation: {}: {}", method.getName(), value);
257 }
258 }
259
260 for (Class<?> s : cls.getInterfaces()) {
261 out.putAll(getMetaMethodsInternal(s));
262 }
263 return out;
264 }
265
266 private static <T extends Annotation> Collection<String> getAnnotatedPropertyKeys(
267 Class<?> cls, Class<T> annotationClass, boolean includeMeta) {
268 List<String> out = Lists.newArrayList();
269 for (Method method : cls.getMethods()) {
270 T unique = method.getAnnotation(annotationClass);
271 if (unique != null) {
272 Property prop = method.getAnnotation(Property.class);
273 if (prop != null && (includeMeta || !prop.value().startsWith("__"))) {
274 out.add(prop.value());
275 }
276 }
277
278 }
279
280 for (Class<?> s : cls.getInterfaces()) {
281 out.addAll(getAnnotatedPropertyKeys(s, annotationClass, includeMeta));
282 }
283
284 return ImmutableSet.copyOf(out);
285 }
286 }