KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > inject > Key


1 /**
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package com.google.inject;
18
19 import static com.google.inject.util.Objects.nonNull;
20 import com.google.inject.util.StackTraceElements;
21 import com.google.inject.util.ToStringBuilder;
22 import com.google.inject.util.Annotations;
23 import java.lang.annotation.Annotation JavaDoc;
24 import java.lang.reflect.Member JavaDoc;
25 import java.lang.reflect.Type JavaDoc;
26
27 /**
28  * Binding key consisting of an injection type and an optional annotation.
29  * Matches the type and annotation at a point of injection.
30  *
31  * <p>For example, {@code Key.get(Service.class, Transactional.class)} will
32  * match:
33  *
34  * <pre>
35  * {@literal @}Inject
36  * public void setService({@literal @}Transactional Service service) {
37  * ...
38  * }
39  * </pre>
40  *
41  * <p>{@code Key} supports generic types via subclassing just like {@link
42  * TypeLiteral}.
43  *
44  * @author crazybob@google.com (Bob Lee)
45  */

46 public abstract class Key<T> {
47
48   final AnnotationStrategy annotationStrategy;
49
50   final TypeLiteral<T> typeLiteral;
51   final int hashCode;
52
53   /**
54    * Constructs a new key. Derives the type from this class's type parameter.
55    *
56    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
57    * parameter in the anonymous class's type hierarchy so we can reconstitute it
58    * at runtime despite erasure.
59    *
60    * <p>Example usage for a binding of type {@code Foo} annotated with
61    * {@code @Bar}:
62    *
63    * <p>{@code new Key<Foo>(Bar.class) {}}.
64    */

65   @SuppressWarnings JavaDoc("unchecked")
66   protected Key(Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
67     this.annotationStrategy = strategyFor(annotationType);
68     this.typeLiteral
69         = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
70     this.hashCode = computeHashCode();
71   }
72
73   /**
74    * Constructs a new key. Derives the type from this class's type parameter.
75    *
76    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
77    * parameter in the anonymous class's type hierarchy so we can reconstitute it
78    * at runtime despite erasure.
79    *
80    * <p>Example usage for a binding of type {@code Foo} annotated with
81    * {@code @Bar}:
82    *
83    * <p>{@code new Key<Foo>(new Bar()) {}}.
84    */

85   @SuppressWarnings JavaDoc("unchecked")
86   protected Key(Annotation JavaDoc annotation) {
87     // no usages, not test-covered
88
this.annotationStrategy = strategyFor(annotation);
89     this.typeLiteral
90         = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
91     this.hashCode = computeHashCode();
92   }
93
94   /**
95    * Constructs a new key. Derives the type from this class's type parameter.
96    *
97    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
98    * parameter in the anonymous class's type hierarchy so we can reconstitute it
99    * at runtime despite erasure.
100    *
101    * <p>Example usage for a binding of type {@code Foo}:
102    *
103    * <p>{@code new Key<Foo>() {}}.
104    */

105   @SuppressWarnings JavaDoc("unchecked")
106   protected Key() {
107     this.annotationStrategy = NULL_STRATEGY;
108     this.typeLiteral
109         = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
110     this.hashCode = computeHashCode();
111   }
112
113   /**
114    * Unsafe. Constructs a key from a manually specified type.
115    */

116   @SuppressWarnings JavaDoc("unchecked")
117   private Key(Type JavaDoc type, AnnotationStrategy annotationStrategy) {
118     this.annotationStrategy = annotationStrategy;
119     this.typeLiteral = (TypeLiteral<T>) TypeLiteral.get(type);
120     this.hashCode = computeHashCode();
121   }
122
123   /** Constructs a key from a manually specified type. */
124   private Key(TypeLiteral<T> typeLiteral,
125       AnnotationStrategy annotationStrategy) {
126     this.annotationStrategy = annotationStrategy;
127     this.typeLiteral = typeLiteral;
128     this.hashCode = computeHashCode();
129   }
130
131   private int computeHashCode() {
132     return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
133   }
134
135   /**
136    * Gets the key type.
137    */

138   public TypeLiteral<T> getTypeLiteral() {
139     return typeLiteral;
140   }
141
142   /**
143    * Gets the annotation type.
144    */

145   public Class JavaDoc<? extends Annotation JavaDoc> getAnnotationType() {
146     return annotationStrategy.getAnnotationType();
147   }
148
149   /**
150    * Gets the annotation.
151    */

152   public Annotation JavaDoc getAnnotation() {
153     return annotationStrategy.getAnnotation();
154   }
155
156   boolean hasAnnotationType() {
157     return annotationStrategy.getAnnotationType() != null;
158   }
159
160   String JavaDoc getAnnotationName() {
161     Annotation JavaDoc annotation = annotationStrategy.getAnnotation();
162     if (annotation != null) {
163       return annotation.toString();
164     }
165
166     // not test-covered
167
return annotationStrategy.getAnnotationType().toString();
168   }
169
170   public int hashCode() {
171     return this.hashCode;
172   }
173
174   Class JavaDoc<? super T> getRawType() {
175     return typeLiteral.getRawType();
176   }
177
178   public boolean equals(Object JavaDoc o) {
179     if (o == this) {
180       return true;
181     }
182     if (!(o instanceof Key<?>)) {
183       return false;
184     }
185     Key<?> other = (Key<?>) o;
186     return annotationStrategy.equals(other.annotationStrategy)
187         && typeLiteral.equals(other.typeLiteral);
188   }
189
190   public String JavaDoc toString() {
191     return new ToStringBuilder(Key.class)
192         .add("type", typeLiteral)
193         .add("annotation", annotationStrategy)
194         .toString();
195   }
196
197   /**
198    * Gets a key for an injection type and an annotation strategy.
199    */

200   static <T> Key<T> get(Class JavaDoc<T> type,
201       AnnotationStrategy annotationStrategy) {
202     return new SimpleKey<T>(type, annotationStrategy);
203   }
204
205   /**
206    * Gets a key for an injection type.
207    */

208   public static <T> Key<T> get(Class JavaDoc<T> type) {
209     return new SimpleKey<T>(type, NULL_STRATEGY);
210   }
211
212   /**
213    * Gets a key for an injection type and an annotation type.
214    */

215   public static <T> Key<T> get(Class JavaDoc<T> type,
216       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
217     return new SimpleKey<T>(type, strategyFor(annotationType));
218   }
219
220   /**
221    * Gets a key for an injection type and an annotation.
222    */

223   public static <T> Key<T> get(Class JavaDoc<T> type, Annotation JavaDoc annotation) {
224     return new SimpleKey<T>(type, strategyFor(annotation));
225   }
226
227   /**
228    * Gets a key for an injection type.
229    */

230   public static Key<?> get(Type JavaDoc type) {
231     return new SimpleKey<Object JavaDoc>(type, NULL_STRATEGY);
232   }
233
234   /**
235    * Gets a key for an injection type and an annotation type.
236    */

237   public static Key<?> get(Type JavaDoc type,
238       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
239     return new SimpleKey<Object JavaDoc>(type, strategyFor(annotationType));
240   }
241
242   /**
243    * Gets a key for an injection type and an annotation.
244    */

245   public static Key<?> get(Type JavaDoc type, Annotation JavaDoc annotation) {
246     return new SimpleKey<Object JavaDoc>(type, strategyFor(annotation));
247   }
248
249   /**
250    * Gets a key for an injection type.
251    */

252   public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
253     return new SimpleKey<T>(typeLiteral, NULL_STRATEGY);
254   }
255
256   /**
257    * Gets a key for an injection type and an annotation type.
258    */

259   public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
260       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
261     return new SimpleKey<T>(typeLiteral, strategyFor(annotationType));
262   }
263
264   /**
265    * Gets a key for an injection type and an annotation.
266    */

267   public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
268       Annotation JavaDoc annotation) {
269     return new SimpleKey<T>(typeLiteral, strategyFor(annotation));
270   }
271
272   /**
273    * Gets a key for the given type, member and annotations.
274    */

275   static Key<?> get(Type JavaDoc type, Member JavaDoc member, Annotation JavaDoc[] annotations,
276       ErrorHandler errorHandler) {
277     Annotation JavaDoc found = null;
278     for (Annotation JavaDoc annotation : annotations) {
279       if (annotation.annotationType().getAnnotation(BindingAnnotation.class) != null) {
280         if (found == null) {
281           found = annotation;
282         } else {
283           errorHandler.handle(StackTraceElements.forMember(member),
284               ErrorMessages.DUPLICATE_ANNOTATIONS, found, annotation);
285         }
286       }
287     }
288     Key<?> key = found == null ? Key.get(type) : Key.get(type, found);
289     return key;
290   }
291
292   /**
293    * Returns a new key of the specified type with the same annotation as this
294    * key.
295    */

296   <T> Key<T> ofType(Class JavaDoc<T> type) {
297     return new SimpleKey<T>(type, annotationStrategy);
298   }
299
300   /**
301    * Returns a new key of the specified type with the same annotation as this
302    * key.
303    */

304   Key<?> ofType(Type JavaDoc type) {
305     return new SimpleKey<Object JavaDoc>(type, annotationStrategy);
306   }
307
308   /**
309    * Returns true if this key has annotation attributes.
310    * @return
311    */

312   boolean hasAttributes() {
313     return annotationStrategy.hasAttributes();
314   }
315
316   /**
317    * Returns this key without annotation attributes, i.e. with only the
318    * annotation type.
319    */

320   Key<T> withoutAttributes() {
321     return new SimpleKey<T>(typeLiteral, annotationStrategy.withoutAttributes());
322   }
323
324   private static class SimpleKey<T> extends Key<T> {
325
326     private SimpleKey(Type JavaDoc type, AnnotationStrategy annotationStrategy) {
327       super(type, annotationStrategy);
328     }
329
330     private SimpleKey(TypeLiteral<T> typeLiteral,
331         AnnotationStrategy annotationStrategy) {
332       super(typeLiteral, annotationStrategy);
333     }
334   }
335
336   interface AnnotationStrategy {
337
338     Annotation JavaDoc getAnnotation();
339     Class JavaDoc<? extends Annotation JavaDoc> getAnnotationType();
340     boolean hasAttributes();
341     AnnotationStrategy withoutAttributes();
342   }
343
344   static final AnnotationStrategy NULL_STRATEGY = new AnnotationStrategy() {
345
346     public boolean hasAttributes() {
347       return false;
348     }
349
350     public AnnotationStrategy withoutAttributes() {
351       throw new UnsupportedOperationException JavaDoc("Key already has no attributes.");
352     }
353
354     public Annotation JavaDoc getAnnotation() {
355       return null;
356     }
357
358     public Class JavaDoc<? extends Annotation JavaDoc> getAnnotationType() {
359       return null;
360     }
361
362     public boolean equals(Object JavaDoc o) {
363       return o == NULL_STRATEGY;
364     }
365
366     public int hashCode() {
367       return 0;
368     }
369
370     public String JavaDoc toString() {
371       return "[none]";
372     }
373   };
374
375   /**
376    * Returns {@code true} if the given annotation type has no attributes.
377    */

378   static boolean isMarker(Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
379     return annotationType.getDeclaredMethods().length == 0;
380   }
381
382   /**
383    * Gets the strategy for an annotation.
384    */

385   static AnnotationStrategy strategyFor(Annotation JavaDoc annotation) {
386     nonNull(annotation, "annotation");
387     Class JavaDoc<? extends Annotation JavaDoc> annotationType = annotation.annotationType();
388     ensureRetainedAtRuntime(annotationType);
389     ensureIsBindingAnnotation(annotationType);
390     return new AnnotationInstanceStrategy(annotation);
391   }
392
393   /**
394    * Gets the strategy for an annotation type.
395    */

396   static AnnotationStrategy strategyFor(
397       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
398     nonNull(annotationType, "annotation type");
399     ensureRetainedAtRuntime(annotationType);
400     ensureIsBindingAnnotation(annotationType);
401     return new AnnotationTypeStrategy(annotationType, null);
402   }
403
404   private static void ensureRetainedAtRuntime(
405       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
406     if (!Annotations.isRetainedAtRuntime(annotationType)) {
407       throw new IllegalArgumentException JavaDoc(annotationType.getName()
408           + " is not retained at runtime."
409           + " Please annotate it with @Retention(RUNTIME).");
410     }
411   }
412
413   private static void ensureIsBindingAnnotation(
414       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
415     if (!isBindingAnnotation(annotationType)) {
416       throw new IllegalArgumentException JavaDoc(annotationType.getName()
417           + " is not a binding annotation."
418           + " Please annotate it with @BindingAnnotation.");
419     }
420   }
421
422   // this class not test-covered
423
static class AnnotationInstanceStrategy implements AnnotationStrategy {
424
425     final Annotation JavaDoc annotation;
426
427     AnnotationInstanceStrategy(Annotation JavaDoc annotation) {
428       this.annotation = nonNull(annotation, "annotation");
429     }
430
431     public boolean hasAttributes() {
432       return true;
433     }
434
435     public AnnotationStrategy withoutAttributes() {
436       return new AnnotationTypeStrategy(getAnnotationType(), annotation);
437     }
438
439     public Annotation JavaDoc getAnnotation() {
440       return annotation;
441     }
442
443     public Class JavaDoc<? extends Annotation JavaDoc> getAnnotationType() {
444       return annotation.annotationType();
445     }
446
447     public boolean equals(Object JavaDoc o) {
448       if (!(o instanceof AnnotationInstanceStrategy)) {
449         return false;
450       }
451
452       AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o;
453       return annotation.equals(other.annotation);
454     }
455
456     public int hashCode() {
457       return annotation.hashCode();
458     }
459
460     public String JavaDoc toString() {
461       return annotation.toString();
462     }
463   }
464
465   static class AnnotationTypeStrategy implements AnnotationStrategy {
466
467     final Class JavaDoc<? extends Annotation JavaDoc> annotationType;
468
469     // Keep the instance around if we have it so the client can request it.
470
final Annotation JavaDoc annotation;
471
472     AnnotationTypeStrategy(Class JavaDoc<? extends Annotation JavaDoc> annotationType,
473         Annotation JavaDoc annotation) {
474       this.annotationType = nonNull(annotationType, "annotation type");
475       this.annotation = annotation;
476     }
477
478     public boolean hasAttributes() {
479       return false;
480     }
481
482     public AnnotationStrategy withoutAttributes() {
483       throw new UnsupportedOperationException JavaDoc("Key already has no attributes.");
484     }
485
486     public Annotation JavaDoc getAnnotation() {
487       return annotation;
488     }
489
490     public Class JavaDoc<? extends Annotation JavaDoc> getAnnotationType() {
491       return annotationType;
492     }
493
494     public boolean equals(Object JavaDoc o) {
495       if (!(o instanceof AnnotationTypeStrategy)) {
496         return false;
497       }
498
499       AnnotationTypeStrategy other = (AnnotationTypeStrategy) o;
500       return annotationType.equals(other.annotationType);
501     }
502
503     public int hashCode() {
504       return annotationType.hashCode();
505     }
506
507     public String JavaDoc toString() {
508       return "@" + annotationType.getName();
509     }
510   }
511
512   static boolean isBindingAnnotation(Annotation JavaDoc annotation) {
513     return isBindingAnnotation(annotation.annotationType());
514   }
515
516   static boolean isBindingAnnotation(
517       Class JavaDoc<? extends Annotation JavaDoc> annotationType) {
518     return annotationType.isAnnotationPresent(BindingAnnotation.class);
519   }
520 }
521
Popular Tags