/**
* 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 static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.Annotations.generateAnnotation;
import static com.google.inject.internal.Annotations.isAllDefaultMethods;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.MoreTypes;
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.
*
*
For example, {@code Key.get(Service.class, Transactional.class)} will
* match:
*
*
* {@literal @}Inject
* public void setService({@literal @}Transactional Service service) {
* ...
* }
*
*
*
{@code Key} supports generic types via subclassing just like {@link
* TypeLiteral}.
*
*
Keys do not differentiate between primitive types (int, char, etc.) and
* their corresponding wrapper types (Integer, Character, etc.). Primitive
* types will be replaced with their wrapper types when keys are created.
*
* @author crazybob@google.com (Bob Lee)
*/
public class Key {
private final AnnotationStrategy annotationStrategy;
private final TypeLiteral typeLiteral;
private final int hashCode;
private final Supplier toStringSupplier;
/**
* Constructs a new key. Derives the type from this class's type parameter.
*
*
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.
*
*
Example usage for a binding of type {@code Foo} annotated with
* {@code @Bar}:
*
*
{@code new Key(Bar.class) {}}.
*/
@SuppressWarnings("unchecked")
protected Key(Class extends Annotation> annotationType) {
this.annotationStrategy = strategyFor(annotationType);
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
* Constructs a new key. Derives the type from this class's type parameter.
*
*
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.
*
*
Example usage for a binding of type {@code Foo} annotated with
* {@code @Bar}:
*
*
{@code new Key(new Bar()) {}}.
*/
@SuppressWarnings("unchecked")
protected Key(Annotation annotation) {
// no usages, not test-covered
this.annotationStrategy = strategyFor(annotation);
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
* Constructs a new key. Derives the type from this class's type parameter.
*
*
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.
*
*
Example usage for a binding of type {@code Foo}:
*
*
{@code new Key() {}}.
*/
@SuppressWarnings("unchecked")
protected Key() {
this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
* 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) TypeLiteral.get(type));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/** Constructs a key from a manually specified type. */
private Key(TypeLiteral typeLiteral, AnnotationStrategy annotationStrategy) {
this.annotationStrategy = annotationStrategy;
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
* Computes the hash code for this key.
*/
private int computeHashCode() {
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
}
/**
* @return a {@link Supplier} which memoizes the value for lazy initialization.
*/
private Supplier createToStringSupplier() {
// The performance hit on access is acceptable since the intended use is for non-performance-
// critical applications such as debugging and logging.
return Suppliers.memoize(new Supplier() {
@Override public String get() {
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
}
});
}
/**
* Gets the key type.
*/
public final TypeLiteral 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> 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 toStringSupplier.get();
}
/**
* Gets a key for an injection type and an annotation strategy.
*/
static Key get(Class type,
AnnotationStrategy annotationStrategy) {
return new Key(type, annotationStrategy);
}
/**
* Gets a key for an injection type.
*/
public static Key get(Class type) {
return new Key(type, NullAnnotationStrategy.INSTANCE);
}
/**
* Gets a key for an injection type and an annotation type.
*/
public static Key get(Class type,
Class extends Annotation> annotationType) {
return new Key(type, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static Key get(Class type, Annotation annotation) {
return new Key(type, strategyFor(annotation));
}
/**
* Gets a key for an injection type.
*/
public static Key> get(Type type) {
return new Key