| /** |
| * Copyright (C) 2006 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject; |
| |
| import com.google.inject.internal.Annotations; |
| import com.google.inject.internal.MoreTypes; |
| import static com.google.inject.internal.Preconditions.checkArgument; |
| import static com.google.inject.internal.Preconditions.checkNotNull; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Type; |
| |
| /** |
| * Binding key consisting of an injection type and an optional annotation. |
| * Matches the type and annotation at a point of injection. |
| * |
| * <p>For example, {@code Key.get(Service.class, Transactional.class)} will |
| * match: |
| * |
| * <pre> |
| * {@literal @}Inject |
| * public void setService({@literal @}Transactional Service service) { |
| * ... |
| * } |
| * </pre> |
| * |
| * <p>{@code Key} supports generic types via subclassing just like {@link |
| * TypeLiteral}. |
| * |
| * <p>Keys do not differentiate between primitive types (int, char, etc.) and |
| * their correpsonding wrapper types (Integer, Character, etc.). Primitive |
| * types will be replaced with their wrapper types when keys are created. |
| * |
| * @author [email protected] (Bob Lee) |
| */ |
| public class Key<T> { |
| |
| private final AnnotationStrategy annotationStrategy; |
| |
| private final TypeLiteral<T> typeLiteral; |
| private final int hashCode; |
| |
| /** |
| * Constructs a new key. Derives the type from this class's type parameter. |
| * |
| * <p>Clients create an empty anonymous subclass. Doing so embeds the type |
| * parameter in the anonymous class's type hierarchy so we can reconstitute it |
| * at runtime despite erasure. |
| * |
| * <p>Example usage for a binding of type {@code Foo} annotated with |
| * {@code @Bar}: |
| * |
| * <p>{@code new Key<Foo>(Bar.class) {}}. |
| */ |
| @SuppressWarnings("unchecked") |
| protected Key(Class<? extends Annotation> annotationType) { |
| this.annotationStrategy = strategyFor(annotationType); |
| this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()); |
| this.hashCode = computeHashCode(); |
| } |
| |
| /** |
| * Constructs a new key. Derives the type from this class's type parameter. |
| * |
| * <p>Clients create an empty anonymous subclass. Doing so embeds the type |
| * parameter in the anonymous class's type hierarchy so we can reconstitute it |
| * at runtime despite erasure. |
| * |
| * <p>Example usage for a binding of type {@code Foo} annotated with |
| * {@code @Bar}: |
| * |
| * <p>{@code new Key<Foo>(new Bar()) {}}. |
| */ |
| @SuppressWarnings("unchecked") |
| protected Key(Annotation annotation) { |
| // no usages, not test-covered |
| this.annotationStrategy = strategyFor(annotation); |
| this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()); |
| this.hashCode = computeHashCode(); |
| } |
| |
| /** |
| * Constructs a new key. Derives the type from this class's type parameter. |
| * |
| * <p>Clients create an empty anonymous subclass. Doing so embeds the type |
| * parameter in the anonymous class's type hierarchy so we can reconstitute it |
| * at runtime despite erasure. |
| * |
| * <p>Example usage for a binding of type {@code Foo}: |
| * |
| * <p>{@code new Key<Foo>() {}}. |
| */ |
| @SuppressWarnings("unchecked") |
| protected Key() { |
| this.annotationStrategy = NullAnnotationStrategy.INSTANCE; |
| this.typeLiteral = (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()); |
| this.hashCode = computeHashCode(); |
| } |
| |
| /** |
| * Unsafe. Constructs a key from a manually specified type. |
| */ |
| @SuppressWarnings("unchecked") |
| private Key(Type type, AnnotationStrategy annotationStrategy) { |
| this.annotationStrategy = annotationStrategy; |
| this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type)); |
| this.hashCode = computeHashCode(); |
| } |
| |
| /** Constructs a key from a manually specified type. */ |
| private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) { |
| this.annotationStrategy = annotationStrategy; |
| this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral); |
| this.hashCode = computeHashCode(); |
| } |
| |
| private int computeHashCode() { |
| return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode(); |
| } |
| |
| /** |
| * Gets the key type. |
| */ |
| public final TypeLiteral<T> getTypeLiteral() { |
| return typeLiteral; |
| } |
| |
| /** |
| * Gets the annotation type. |
| */ |
| public final Class<? extends Annotation> getAnnotationType() { |
| return annotationStrategy.getAnnotationType(); |
| } |
| |
| /** |
| * Gets the annotation. |
| */ |
| public final Annotation getAnnotation() { |
| return annotationStrategy.getAnnotation(); |
| } |
| |
| boolean hasAnnotationType() { |
| return annotationStrategy.getAnnotationType() != null; |
| } |
| |
| String getAnnotationName() { |
| Annotation annotation = annotationStrategy.getAnnotation(); |
| if (annotation != null) { |
| return annotation.toString(); |
| } |
| |
| // not test-covered |
| return annotationStrategy.getAnnotationType().toString(); |
| } |
| |
| Class<? super T> getRawType() { |
| return typeLiteral.getRawType(); |
| } |
| |
| /** |
| * Gets the key of this key's provider. |
| */ |
| Key<Provider<T>> providerKey() { |
| return ofType(typeLiteral.providerType()); |
| } |
| |
| @Override public final boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof Key<?>)) { |
| return false; |
| } |
| Key<?> other = (Key<?>) o; |
| return annotationStrategy.equals(other.annotationStrategy) |
| && typeLiteral.equals(other.typeLiteral); |
| } |
| |
| @Override public final int hashCode() { |
| return this.hashCode; |
| } |
| |
| @Override public final String toString() { |
| return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]"; |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation strategy. |
| */ |
| static <T> Key<T> get(Class<T> type, |
| AnnotationStrategy annotationStrategy) { |
| return new Key<T>(type, annotationStrategy); |
| } |
| |
| /** |
| * Gets a key for an injection type. |
| */ |
| public static <T> Key<T> get(Class<T> type) { |
| return new Key<T>(type, NullAnnotationStrategy.INSTANCE); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation type. |
| */ |
| public static <T> Key<T> get(Class<T> type, |
| Class<? extends Annotation> annotationType) { |
| return new Key<T>(type, strategyFor(annotationType)); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation. |
| */ |
| public static <T> Key<T> get(Class<T> type, Annotation annotation) { |
| return new Key<T>(type, strategyFor(annotation)); |
| } |
| |
| /** |
| * Gets a key for an injection type. |
| */ |
| public static Key<?> get(Type type) { |
| return new Key<Object>(type, NullAnnotationStrategy.INSTANCE); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation type. |
| */ |
| public static Key<?> get(Type type, |
| Class<? extends Annotation> annotationType) { |
| return new Key<Object>(type, strategyFor(annotationType)); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation. |
| */ |
| public static Key<?> get(Type type, Annotation annotation) { |
| return new Key<Object>(type, strategyFor(annotation)); |
| } |
| |
| /** |
| * Gets a key for an injection type. |
| */ |
| public static <T> Key<T> get(TypeLiteral<T> typeLiteral) { |
| return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation type. |
| */ |
| public static <T> Key<T> get(TypeLiteral<T> typeLiteral, |
| Class<? extends Annotation> annotationType) { |
| return new Key<T>(typeLiteral, strategyFor(annotationType)); |
| } |
| |
| /** |
| * Gets a key for an injection type and an annotation. |
| */ |
| public static <T> Key<T> get(TypeLiteral<T> typeLiteral, |
| Annotation annotation) { |
| return new Key<T>(typeLiteral, strategyFor(annotation)); |
| } |
| |
| /** |
| * Returns a new key of the specified type with the same annotation as this |
| * key. |
| */ |
| public <T> Key<T> ofType(Class<T> type) { |
| return new Key<T>(type, annotationStrategy); |
| } |
| |
| /** |
| * Returns a new key of the specified type with the same annotation as this |
| * key. |
| */ |
| public Key<?> ofType(Type type) { |
| return new Key<Object>(type, annotationStrategy); |
| } |
| |
| /** |
| * Returns a new key of the specified type with the same annotation as this |
| * key. |
| */ |
| public <T> Key<T> ofType(TypeLiteral<T> type) { |
| return new Key<T>(type, annotationStrategy); |
| } |
| |
| /** |
| * Returns true if this key has annotation attributes. |
| */ |
| public boolean hasAttributes() { |
| return annotationStrategy.hasAttributes(); |
| } |
| |
| /** |
| * Returns this key without annotation attributes, i.e. with only the |
| * annotation type. |
| */ |
| public Key<T> withoutAttributes() { |
| return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes()); |
| } |
| |
| interface AnnotationStrategy { |
| Annotation getAnnotation(); |
| Class<? extends Annotation> getAnnotationType(); |
| boolean hasAttributes(); |
| AnnotationStrategy withoutAttributes(); |
| } |
| |
| /** |
| * Returns {@code true} if the given annotation type has no attributes. |
| */ |
| static boolean isMarker(Class<? extends Annotation> annotationType) { |
| return annotationType.getDeclaredMethods().length == 0; |
| } |
| |
| /** |
| * Gets the strategy for an annotation. |
| */ |
| static AnnotationStrategy strategyFor(Annotation annotation) { |
| checkNotNull(annotation, "annotation"); |
| Class<? extends Annotation> annotationType = annotation.annotationType(); |
| ensureRetainedAtRuntime(annotationType); |
| ensureIsBindingAnnotation(annotationType); |
| |
| if (annotationType.getDeclaredMethods().length == 0) { |
| return new AnnotationTypeStrategy(annotationType, annotation); |
| } |
| |
| return new AnnotationInstanceStrategy(annotation); |
| } |
| |
| /** |
| * Gets the strategy for an annotation type. |
| */ |
| static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) { |
| checkNotNull(annotationType, "annotation type"); |
| ensureRetainedAtRuntime(annotationType); |
| ensureIsBindingAnnotation(annotationType); |
| return new AnnotationTypeStrategy(annotationType, null); |
| } |
| |
| private static void ensureRetainedAtRuntime( |
| Class<? extends Annotation> annotationType) { |
| checkArgument(Annotations.isRetainedAtRuntime(annotationType), |
| "%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).", |
| annotationType.getName()); |
| } |
| |
| private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) { |
| checkArgument(Annotations.isBindingAnnotation(annotationType), |
| "%s is not a binding annotation. Please annotate it with @BindingAnnotation.", |
| annotationType.getName()); |
| } |
| |
| static enum NullAnnotationStrategy implements AnnotationStrategy { |
| INSTANCE; |
| |
| public boolean hasAttributes() { |
| return false; |
| } |
| |
| public AnnotationStrategy withoutAttributes() { |
| throw new UnsupportedOperationException("Key already has no attributes."); |
| } |
| |
| public Annotation getAnnotation() { |
| return null; |
| } |
| |
| public Class<? extends Annotation> getAnnotationType() { |
| return null; |
| } |
| |
| @Override public String toString() { |
| return "[none]"; |
| } |
| } |
| |
| // this class not test-covered |
| static class AnnotationInstanceStrategy implements AnnotationStrategy { |
| |
| final Annotation annotation; |
| |
| AnnotationInstanceStrategy(Annotation annotation) { |
| this.annotation = checkNotNull(annotation, "annotation"); |
| } |
| |
| public boolean hasAttributes() { |
| return true; |
| } |
| |
| public AnnotationStrategy withoutAttributes() { |
| return new AnnotationTypeStrategy(getAnnotationType(), annotation); |
| } |
| |
| public Annotation getAnnotation() { |
| return annotation; |
| } |
| |
| public Class<? extends Annotation> getAnnotationType() { |
| return annotation.annotationType(); |
| } |
| |
| @Override public boolean equals(Object o) { |
| if (!(o instanceof AnnotationInstanceStrategy)) { |
| return false; |
| } |
| |
| AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o; |
| return annotation.equals(other.annotation); |
| } |
| |
| @Override public int hashCode() { |
| return annotation.hashCode(); |
| } |
| |
| @Override public String toString() { |
| return annotation.toString(); |
| } |
| } |
| |
| static class AnnotationTypeStrategy implements AnnotationStrategy { |
| |
| final Class<? extends Annotation> annotationType; |
| |
| // Keep the instance around if we have it so the client can request it. |
| final Annotation annotation; |
| |
| AnnotationTypeStrategy(Class<? extends Annotation> annotationType, |
| Annotation annotation) { |
| this.annotationType = checkNotNull(annotationType, "annotation type"); |
| this.annotation = annotation; |
| } |
| |
| public boolean hasAttributes() { |
| return false; |
| } |
| |
| public AnnotationStrategy withoutAttributes() { |
| throw new UnsupportedOperationException("Key already has no attributes."); |
| } |
| |
| public Annotation getAnnotation() { |
| return annotation; |
| } |
| |
| public Class<? extends Annotation> getAnnotationType() { |
| return annotationType; |
| } |
| |
| @Override public boolean equals(Object o) { |
| if (!(o instanceof AnnotationTypeStrategy)) { |
| return false; |
| } |
| |
| AnnotationTypeStrategy other = (AnnotationTypeStrategy) o; |
| return annotationType.equals(other.annotationType); |
| } |
| |
| @Override public int hashCode() { |
| return annotationType.hashCode(); |
| } |
| |
| @Override public String toString() { |
| return "@" + annotationType.getName(); |
| } |
| } |
| } |