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