1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.reflect;
21
22 import com.google.common.collect.Sets;
23 import org.grouplens.grapht.annotation.AliasFor;
24 import org.grouplens.grapht.annotation.AllowUnqualifiedMatch;
25 import org.grouplens.grapht.util.ClassProxy;
26 import org.grouplens.grapht.util.Preconditions;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import javax.annotation.Nonnull;
31 import javax.inject.Qualifier;
32 import java.io.InvalidObjectException;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectStreamException;
35 import java.io.Serializable;
36 import java.lang.annotation.Annotation;
37 import java.util.Set;
38
39
40
41
42
43
44 public final class Qualifiers {
45 private static final Logger logger = LoggerFactory.getLogger(Qualifiers.class);
46 private Qualifiers() { }
47
48
49
50
51
52
53
54
55
56 public static boolean isQualifier(Class<? extends Annotation> type) {
57 return type.getAnnotation(javax.inject.Qualifier.class) != null;
58 }
59
60
61
62
63
64
65
66
67
68
69
70 @Nonnull
71 public static Class<? extends Annotation> resolveAliases(@Nonnull Class<? extends Annotation> type) {
72 Preconditions.notNull("qualifier type", type);
73 Set<Class<? extends Annotation>> seen = Sets.newHashSet();
74 seen.add(type);
75 Class<? extends Annotation> result = type;
76 AliasFor alias;
77 while ((alias = result.getAnnotation(AliasFor.class)) != null) {
78 if (result.getDeclaredMethods().length > 0) {
79 throw new IllegalArgumentException("aliased qualifier cannot have parameters");
80 }
81 result = alias.value();
82 if (!result.isAnnotationPresent(Qualifier.class)) {
83 throw new IllegalArgumentException("alias target " + type + " is not a qualifier");
84 }
85 if (!seen.add(result)) {
86 throw new IllegalArgumentException("Circular alias reference starting with " + type);
87 }
88 }
89 return result;
90 }
91
92
93
94
95
96 public static QualifierMatcher matchDefault() {
97 return new DefaultMatcher();
98 }
99
100
101
102
103 public static QualifierMatcher matchAny() {
104 return new AnyMatcher();
105 }
106
107
108
109
110 public static QualifierMatcher matchNone() {
111 return new NullMatcher();
112 }
113
114
115
116
117
118
119
120 public static QualifierMatcher match(Class<? extends Annotation> annotType) {
121 if (annotType == null) {
122 return matchNone();
123 } else {
124 return new AnnotationClassMatcher(annotType);
125 }
126 }
127
128
129
130
131
132 public static QualifierMatcher match(Annotation annot) {
133 if (annot == null) {
134 return matchNone();
135 } else if (annot.annotationType().getDeclaredMethods().length == 0) {
136 logger.debug("using type matcher for nullary annotation {}", annot);
137
138
139
140 return new AnnotationClassMatcher(annot.annotationType(),
141 DefaultMatcherPriority.MATCH_VALUE);
142 } else {
143 return new AnnotationMatcher(annot);
144 }
145 }
146
147 private enum DefaultMatcherPriority {
148 MATCH_VALUE,
149 MATCH_TYPE,
150 MATCH_ANY,
151 MATCH_DEFAULT
152 }
153
154 private abstract static class AbstractMatcher implements QualifierMatcher {
155 private static final long serialVersionUID = 1L;
156 private final DefaultMatcherPriority priority;
157
158 AbstractMatcher(DefaultMatcherPriority prio) {
159 priority = prio;
160 }
161
162 @Override
163 public final int getPriority() {
164 return priority.ordinal();
165 }
166
167 @Override
168 @Deprecated
169 public boolean matches(Annotation q) {
170 return apply(q);
171 }
172
173 @Override
174 public int compareTo(QualifierMatcher o) {
175 if (o == null) {
176
177 return 1;
178 } else {
179
180 return getPriority() - o.getPriority();
181 }
182 }
183 }
184
185 private static class DefaultMatcher extends AbstractMatcher {
186 private static final long serialVersionUID = 1L;
187
188 DefaultMatcher() {
189 super(DefaultMatcherPriority.MATCH_DEFAULT);
190 }
191
192 @Override
193 public boolean apply(Annotation q) {
194 if (q == null) {
195 return true;
196 } else {
197 Class<? extends Annotation> atype = q.annotationType();
198 return atype.isAnnotationPresent(AllowUnqualifiedMatch.class);
199 }
200 }
201
202 @Override
203 public boolean equals(Object o) {
204 return o instanceof DefaultMatcher;
205 }
206
207 @Override
208 public int hashCode() {
209 return DefaultMatcher.class.hashCode();
210 }
211
212 @Override
213 public String toString() {
214 return "%";
215 }
216 }
217
218 private static class AnyMatcher extends AbstractMatcher {
219 private static final long serialVersionUID = 1L;
220
221 AnyMatcher() {
222 super(DefaultMatcherPriority.MATCH_ANY);
223 }
224
225 @Override
226 public boolean apply(Annotation q) {
227 return true;
228 }
229
230 @Override
231 public boolean equals(Object o) {
232 return o instanceof AnyMatcher;
233 }
234
235 @Override
236 public int hashCode() {
237 return AnyMatcher.class.hashCode();
238 }
239
240 @Override
241 public String toString() {
242 return "*";
243 }
244 }
245
246 private static class NullMatcher extends AbstractMatcher {
247 private static final long serialVersionUID = 1L;
248
249 NullMatcher() {
250 super(DefaultMatcherPriority.MATCH_VALUE);
251 }
252
253 @Override
254 public boolean apply(Annotation q) {
255 return q == null;
256 }
257
258 @Override
259 public boolean equals(Object o) {
260 return o instanceof NullMatcher;
261 }
262
263 @Override
264 public int hashCode() {
265 return NullMatcher.class.hashCode();
266 }
267
268 @Override
269 public String toString() {
270 return "-";
271 }
272 }
273
274 static class AnnotationClassMatcher extends AbstractMatcher {
275 private static final long serialVersionUID = -1L;
276 private final Class<? extends Annotation> type;
277 private final Class<? extends Annotation> actual;
278
279 public AnnotationClassMatcher(Class<? extends Annotation> type) {
280 this(type, DefaultMatcherPriority.MATCH_TYPE);
281 }
282
283 public AnnotationClassMatcher(Class<? extends Annotation> type,
284 DefaultMatcherPriority prio) {
285 super(prio);
286 Preconditions.notNull("type", type);
287 Preconditions.isQualifier(type);
288 this.type = type;
289
290 actual = resolveAliases(type);
291 }
292
293 @Override
294 public boolean apply(Annotation q) {
295
296 Class<? extends Annotation> qtype = (q == null ? null : q.annotationType());
297 if (qtype == null) {
298 return false;
299 } else {
300 Class<? extends Annotation> qact = resolveAliases(qtype);
301 return actual.equals(qact);
302 }
303 }
304
305 @Override
306 public boolean equals(Object o) {
307 return o instanceof AnnotationClassMatcher
308 && ((AnnotationClassMatcher) o).actual.equals(actual);
309 }
310
311 @Override
312 public int hashCode() {
313 return actual.hashCode();
314 }
315
316 @Override
317 public String toString() {
318 if (type.equals(actual)) {
319 return type.toString();
320 } else {
321 return type.toString() + "( alias of " + actual.toString() + ")";
322 }
323 }
324
325 private Object writeReplace() {
326
327 return new SerialProxy(type);
328 }
329
330 private void readObject(ObjectInputStream stream) throws ObjectStreamException {
331 throw new InvalidObjectException("must use serialization proxy");
332 }
333
334 static class SerialProxy implements Serializable {
335 private static final long serialVersionUID = 1L;
336
337 private final ClassProxy type;
338
339 public SerialProxy(Class<?> cls) {
340 type = ClassProxy.of(cls);
341 }
342
343 private Object readResolve() throws ObjectStreamException {
344 try {
345 return new AnnotationClassMatcher(type.resolve().asSubclass(Annotation.class));
346 } catch (ClassNotFoundException e) {
347 InvalidObjectException ex = new InvalidObjectException("cannot resolve " + type);
348 ex.initCause(e);
349 throw ex;
350 } catch (ClassCastException e) {
351 InvalidObjectException ex =
352 new InvalidObjectException("class " + type + " not an annotation");
353 ex.initCause(e);
354 throw ex;
355 }
356 }
357 }
358 }
359
360 private static class AnnotationMatcher extends AbstractMatcher implements Serializable {
361 private static final long serialVersionUID = 1L;
362
363 @SuppressWarnings("squid:S1948")
364 private final Annotation annotation;
365
366 public AnnotationMatcher(Annotation annot) {
367 super(DefaultMatcherPriority.MATCH_VALUE);
368 Preconditions.notNull("annotation", annot);
369 Preconditions.isQualifier(annot.annotationType());
370 annotation = annot;
371 }
372
373 @Override
374 public boolean apply(Annotation q) {
375 return annotation.equals(q);
376 }
377
378 @Override
379 public boolean equals(Object o) {
380 return (o instanceof AnnotationMatcher)
381 && ((AnnotationMatcher) o).annotation.equals(annotation);
382 }
383
384 @Override
385 public int hashCode() {
386 return annotation.hashCode();
387 }
388
389 @Override
390 public String toString() {
391 return annotation.toString();
392 }
393 }
394 }