• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.inject.internal.Annotations.generateAnnotation;
22 import static com.google.inject.internal.Annotations.isAllDefaultMethods;
23 
24 import com.google.common.base.Supplier;
25 import com.google.common.base.Suppliers;
26 import com.google.inject.internal.Annotations;
27 import com.google.inject.internal.MoreTypes;
28 
29 import java.lang.annotation.Annotation;
30 import java.lang.reflect.Type;
31 
32 /**
33  * Binding key consisting of an injection type and an optional annotation.
34  * Matches the type and annotation at a point of injection.
35  *
36  * <p>For example, {@code Key.get(Service.class, Transactional.class)} will
37  * match:
38  *
39  * <pre>
40  *   {@literal @}Inject
41  *   public void setService({@literal @}Transactional Service service) {
42  *     ...
43  *   }
44  * </pre>
45  *
46  * <p>{@code Key} supports generic types via subclassing just like {@link
47  * TypeLiteral}.
48  *
49  * <p>Keys do not differentiate between primitive types (int, char, etc.) and
50  * their corresponding wrapper types (Integer, Character, etc.). Primitive
51  * types will be replaced with their wrapper types when keys are created.
52  *
53  * @author crazybob@google.com (Bob Lee)
54  */
55 public class Key<T> {
56 
57   private final AnnotationStrategy annotationStrategy;
58 
59   private final TypeLiteral<T> typeLiteral;
60   private final int hashCode;
61   private final Supplier<String> toStringSupplier;
62 
63   /**
64    * Constructs a new key. Derives the type from this class's type parameter.
65    *
66    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
67    * parameter in the anonymous class's type hierarchy so we can reconstitute it
68    * at runtime despite erasure.
69    *
70    * <p>Example usage for a binding of type {@code Foo} annotated with
71    * {@code @Bar}:
72    *
73    * <p>{@code new Key<Foo>(Bar.class) {}}.
74    */
75   @SuppressWarnings("unchecked")
Key(Class<? extends Annotation> annotationType)76   protected Key(Class<? extends Annotation> annotationType) {
77     this.annotationStrategy = strategyFor(annotationType);
78     this.typeLiteral = MoreTypes.canonicalizeForKey(
79         (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
80     this.hashCode = computeHashCode();
81     this.toStringSupplier = createToStringSupplier();
82   }
83 
84   /**
85    * Constructs a new key. Derives the type from this class's type parameter.
86    *
87    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
88    * parameter in the anonymous class's type hierarchy so we can reconstitute it
89    * at runtime despite erasure.
90    *
91    * <p>Example usage for a binding of type {@code Foo} annotated with
92    * {@code @Bar}:
93    *
94    * <p>{@code new Key<Foo>(new Bar()) {}}.
95    */
96   @SuppressWarnings("unchecked")
Key(Annotation annotation)97   protected Key(Annotation annotation) {
98     // no usages, not test-covered
99     this.annotationStrategy = strategyFor(annotation);
100     this.typeLiteral = MoreTypes.canonicalizeForKey(
101         (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
102     this.hashCode = computeHashCode();
103     this.toStringSupplier = createToStringSupplier();
104   }
105 
106   /**
107    * Constructs a new key. Derives the type from this class's type parameter.
108    *
109    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
110    * parameter in the anonymous class's type hierarchy so we can reconstitute it
111    * at runtime despite erasure.
112    *
113    * <p>Example usage for a binding of type {@code Foo}:
114    *
115    * <p>{@code new Key<Foo>() {}}.
116    */
117   @SuppressWarnings("unchecked")
Key()118   protected Key() {
119     this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
120     this.typeLiteral = MoreTypes.canonicalizeForKey(
121         (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
122     this.hashCode = computeHashCode();
123     this.toStringSupplier = createToStringSupplier();
124   }
125 
126   /**
127    * Unsafe. Constructs a key from a manually specified type.
128    */
129   @SuppressWarnings("unchecked")
Key(Type type, AnnotationStrategy annotationStrategy)130   private Key(Type type, AnnotationStrategy annotationStrategy) {
131     this.annotationStrategy = annotationStrategy;
132     this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
133     this.hashCode = computeHashCode();
134     this.toStringSupplier = createToStringSupplier();
135   }
136 
137   /** Constructs a key from a manually specified type. */
Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy)138   private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) {
139     this.annotationStrategy = annotationStrategy;
140     this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
141     this.hashCode = computeHashCode();
142     this.toStringSupplier = createToStringSupplier();
143   }
144 
145   /**
146    * Computes the hash code for this key.
147    */
computeHashCode()148   private int computeHashCode() {
149     return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
150   }
151 
152   /**
153    * @return a {@link Supplier} which memoizes the value for lazy initialization.
154    */
createToStringSupplier()155   private Supplier<String> createToStringSupplier() {
156     // The performance hit on access is acceptable since the intended use is for non-performance-
157     // critical applications such as debugging and logging.
158     return Suppliers.memoize(new Supplier<String>() {
159       @Override public String get() {
160         return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
161       }
162     });
163   }
164 
165   /**
166    * Gets the key type.
167    */
168   public final TypeLiteral<T> getTypeLiteral() {
169     return typeLiteral;
170   }
171 
172   /**
173    * Gets the annotation type.
174    */
175   public final Class<? extends Annotation> getAnnotationType() {
176     return annotationStrategy.getAnnotationType();
177   }
178 
179   /**
180    * Gets the annotation.
181    */
182   public final Annotation getAnnotation() {
183     return annotationStrategy.getAnnotation();
184   }
185 
186   boolean hasAnnotationType() {
187     return annotationStrategy.getAnnotationType() != null;
188   }
189 
190   String getAnnotationName() {
191     Annotation annotation = annotationStrategy.getAnnotation();
192     if (annotation != null) {
193       return annotation.toString();
194     }
195 
196     // not test-covered
197     return annotationStrategy.getAnnotationType().toString();
198   }
199 
200   Class<? super T> getRawType() {
201     return typeLiteral.getRawType();
202   }
203 
204   /**
205    * Gets the key of this key's provider.
206    */
207   Key<Provider<T>> providerKey() {
208     return ofType(typeLiteral.providerType());
209   }
210 
211   @Override public final boolean equals(Object o) {
212     if (o == this) {
213       return true;
214     }
215     if (!(o instanceof Key<?>)) {
216       return false;
217     }
218     Key<?> other = (Key<?>) o;
219     return annotationStrategy.equals(other.annotationStrategy)
220         && typeLiteral.equals(other.typeLiteral);
221   }
222 
223   @Override public final int hashCode() {
224     return this.hashCode;
225   }
226 
227   @Override public final String toString() {
228     return toStringSupplier.get();
229   }
230 
231   /**
232    * Gets a key for an injection type and an annotation strategy.
233    */
234   static <T> Key<T> get(Class<T> type,
235       AnnotationStrategy annotationStrategy) {
236     return new Key<T>(type, annotationStrategy);
237   }
238 
239   /**
240    * Gets a key for an injection type.
241    */
242   public static <T> Key<T> get(Class<T> type) {
243     return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
244   }
245 
246   /**
247    * Gets a key for an injection type and an annotation type.
248    */
249   public static <T> Key<T> get(Class<T> type,
250       Class<? extends Annotation> annotationType) {
251     return new Key<T>(type, strategyFor(annotationType));
252   }
253 
254   /**
255    * Gets a key for an injection type and an annotation.
256    */
257   public static <T> Key<T> get(Class<T> type, Annotation annotation) {
258     return new Key<T>(type, strategyFor(annotation));
259   }
260 
261   /**
262    * Gets a key for an injection type.
263    */
264   public static Key<?> get(Type type) {
265     return new Key<Object>(type, NullAnnotationStrategy.INSTANCE);
266   }
267 
268   /**
269    * Gets a key for an injection type and an annotation type.
270    */
271   public static Key<?> get(Type type,
272       Class<? extends Annotation> annotationType) {
273     return new Key<Object>(type, strategyFor(annotationType));
274   }
275 
276   /**
277    * Gets a key for an injection type and an annotation.
278    */
279   public static Key<?> get(Type type, Annotation annotation) {
280     return new Key<Object>(type, strategyFor(annotation));
281   }
282 
283   /**
284    * Gets a key for an injection type.
285    */
286   public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
287     return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
288   }
289 
290   /**
291    * Gets a key for an injection type and an annotation type.
292    */
293   public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
294       Class<? extends Annotation> annotationType) {
295     return new Key<T>(typeLiteral, strategyFor(annotationType));
296   }
297 
298   /**
299    * Gets a key for an injection type and an annotation.
300    */
301   public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
302       Annotation annotation) {
303     return new Key<T>(typeLiteral, strategyFor(annotation));
304   }
305 
306   /**
307    * Returns a new key of the specified type with the same annotation as this
308    * key.
309    *
310    * @since 3.0
311    */
312   public <T> Key<T> ofType(Class<T> type) {
313     return new Key<T>(type, annotationStrategy);
314   }
315 
316   /**
317    * Returns a new key of the specified type with the same annotation as this
318    * key.
319    *
320    * @since 3.0
321    */
322   public Key<?> ofType(Type type) {
323     return new Key<Object>(type, annotationStrategy);
324   }
325 
326   /**
327    * Returns a new key of the specified type with the same annotation as this
328    * key.
329    *
330    * @since 3.0
331    */
332   public <T> Key<T> ofType(TypeLiteral<T> type) {
333     return new Key<T>(type, annotationStrategy);
334   }
335 
336   /**
337    * Returns true if this key has annotation attributes.
338    *
339    * @since 3.0
340    */
341   public boolean hasAttributes() {
342     return annotationStrategy.hasAttributes();
343   }
344 
345   /**
346    * Returns this key without annotation attributes, i.e. with only the
347    * annotation type.
348    *
349    * @since 3.0
350    */
351   public Key<T> withoutAttributes() {
352     return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
353   }
354 
355   interface AnnotationStrategy {
356     Annotation getAnnotation();
357     Class<? extends Annotation> getAnnotationType();
358     boolean hasAttributes();
359     AnnotationStrategy withoutAttributes();
360   }
361 
362   /**
363    * Gets the strategy for an annotation.
364    */
365   static AnnotationStrategy strategyFor(Annotation annotation) {
366     checkNotNull(annotation, "annotation");
367     Class<? extends Annotation> annotationType = annotation.annotationType();
368     ensureRetainedAtRuntime(annotationType);
369     ensureIsBindingAnnotation(annotationType);
370 
371     if (Annotations.isMarker(annotationType)) {
372       return new AnnotationTypeStrategy(annotationType, annotation);
373     }
374 
375     return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
376   }
377 
378   /**
379    * Gets the strategy for an annotation type.
380    */
381   static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
382     annotationType = Annotations.canonicalizeIfNamed(annotationType);
383     if (isAllDefaultMethods(annotationType)) {
384       return strategyFor(generateAnnotation(annotationType));
385     }
386 
387     checkNotNull(annotationType, "annotation type");
388     ensureRetainedAtRuntime(annotationType);
389     ensureIsBindingAnnotation(annotationType);
390     return new AnnotationTypeStrategy(annotationType, null);
391 
392   }
393 
394   private static void ensureRetainedAtRuntime(
395       Class<? extends Annotation> annotationType) {
396     checkArgument(Annotations.isRetainedAtRuntime(annotationType),
397         "%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
398         annotationType.getName());
399   }
400 
401   private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
402     checkArgument(Annotations.isBindingAnnotation(annotationType),
403         "%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
404         annotationType.getName());
405   }
406 
407   static enum NullAnnotationStrategy implements AnnotationStrategy {
408     INSTANCE;
409 
410     public boolean hasAttributes() {
411       return false;
412     }
413 
414     public AnnotationStrategy withoutAttributes() {
415       throw new UnsupportedOperationException("Key already has no attributes.");
416     }
417 
418     public Annotation getAnnotation() {
419       return null;
420     }
421 
422     public Class<? extends Annotation> getAnnotationType() {
423       return null;
424     }
425 
426     @Override public String toString() {
427       return "[none]";
428     }
429   }
430 
431   // this class not test-covered
432   static class AnnotationInstanceStrategy implements AnnotationStrategy {
433 
434     final Annotation annotation;
435 
436     AnnotationInstanceStrategy(Annotation annotation) {
437       this.annotation = checkNotNull(annotation, "annotation");
438     }
439 
440     public boolean hasAttributes() {
441       return true;
442     }
443 
444     public AnnotationStrategy withoutAttributes() {
445       return new AnnotationTypeStrategy(getAnnotationType(), annotation);
446     }
447 
448     public Annotation getAnnotation() {
449       return annotation;
450     }
451 
452     public Class<? extends Annotation> getAnnotationType() {
453       return annotation.annotationType();
454     }
455 
456     @Override public boolean equals(Object o) {
457       if (!(o instanceof AnnotationInstanceStrategy)) {
458         return false;
459       }
460 
461       AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o;
462       return annotation.equals(other.annotation);
463     }
464 
465     @Override public int hashCode() {
466       return annotation.hashCode();
467     }
468 
469     @Override public String toString() {
470       return annotation.toString();
471     }
472   }
473 
474   static class AnnotationTypeStrategy implements AnnotationStrategy {
475 
476     final Class<? extends Annotation> annotationType;
477 
478     // Keep the instance around if we have it so the client can request it.
479     final Annotation annotation;
480 
481     AnnotationTypeStrategy(Class<? extends Annotation> annotationType,
482         Annotation annotation) {
483       this.annotationType = checkNotNull(annotationType, "annotation type");
484       this.annotation = annotation;
485     }
486 
487     public boolean hasAttributes() {
488       return false;
489     }
490 
491     public AnnotationStrategy withoutAttributes() {
492       throw new UnsupportedOperationException("Key already has no attributes.");
493     }
494 
495     public Annotation getAnnotation() {
496       return annotation;
497     }
498 
499     public Class<? extends Annotation> getAnnotationType() {
500       return annotationType;
501     }
502 
503     @Override public boolean equals(Object o) {
504       if (!(o instanceof AnnotationTypeStrategy)) {
505         return false;
506       }
507 
508       AnnotationTypeStrategy other = (AnnotationTypeStrategy) o;
509       return annotationType.equals(other.annotationType);
510     }
511 
512     @Override public int hashCode() {
513       return annotationType.hashCode();
514     }
515 
516     @Override public String toString() {
517       return "@" + annotationType.getName();
518     }
519   }
520 }
521