Package com.google.inject

Source Code of com.google.inject.Key

/**
* 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.inject.util.Objects.nonNull;
import com.google.inject.util.StackTraceElements;
import com.google.inject.util.ToStringBuilder;
import com.google.inject.util.Annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
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}.
*
* @author crazybob@google.com (Bob Lee)
*/
public abstract class Key<T> {

  final AnnotationStrategy annotationStrategy;

  final TypeLiteral<T> typeLiteral;
  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 = NULL_STRATEGY;
    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 = (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 = typeLiteral;
    this.hashCode = computeHashCode();
  }

  private int computeHashCode() {
    return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
  }

  /**
   * Gets the key type.
   */
  public TypeLiteral<T> getTypeLiteral() {
    return typeLiteral;
  }

  /**
   * Gets the annotation type.
   */
  public Class<? extends Annotation> getAnnotationType() {
    return annotationStrategy.getAnnotationType();
  }

  /**
   * Gets the annotation.
   */
  public 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();
  }

  public int hashCode() {
    return this.hashCode;
  }

  Class<? super T> getRawType() {
    return typeLiteral.getRawType();
  }

  public 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);
  }

  public String toString() {
    return new ToStringBuilder(Key.class)
        .add("type", typeLiteral)
        .add("annotation", annotationStrategy)
        .toString();
  }

  /**
   * Gets a key for an injection type and an annotation strategy.
   */
  static <T> Key<T> get(Class<T> type,
      AnnotationStrategy annotationStrategy) {
    return new SimpleKey<T>(type, annotationStrategy);
  }

  /**
   * Gets a key for an injection type.
   */
  public static <T> Key<T> get(Class<T> type) {
    return new SimpleKey<T>(type, NULL_STRATEGY);
  }

  /**
   * 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 SimpleKey<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 SimpleKey<T>(type, strategyFor(annotation));
  }

  /**
   * Gets a key for an injection type.
   */
  public static Key<?> get(Type type) {
    return new SimpleKey<Object>(type, NULL_STRATEGY);
  }

  /**
   * Gets a key for an injection type and an annotation type.
   */
  public static Key<?> get(Type type,
      Class<? extends Annotation> annotationType) {
    return new SimpleKey<Object>(type, strategyFor(annotationType));
  }

  /**
   * Gets a key for an injection type and an annotation.
   */
  public static Key<?> get(Type type, Annotation annotation) {
    return new SimpleKey<Object>(type, strategyFor(annotation));
  }

  /**
   * Gets a key for an injection type.
   */
  public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
    return new SimpleKey<T>(typeLiteral, NULL_STRATEGY);
  }

  /**
   * 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 SimpleKey<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 SimpleKey<T>(typeLiteral, strategyFor(annotation));
  }

  /**
   * Gets a key for the given type, member and annotations.
   */
  static Key<?> get(Type type, Member member, Annotation[] annotations,
      ErrorHandler errorHandler) {
    Annotation found = null;
    for (Annotation annotation : annotations) {
      if (annotation.annotationType().getAnnotation(BindingAnnotation.class) != null) {
        if (found == null) {
          found = annotation;
        } else {
          errorHandler.handle(StackTraceElements.forMember(member),
              ErrorMessages.DUPLICATE_ANNOTATIONS, found, annotation);
        }
      }
    }
    Key<?> key = found == null ? Key.get(type) : Key.get(type, found);
    return key;
  }

  /**
   * Returns a new key of the specified type with the same annotation as this
   * key.
   */
  <T> Key<T> ofType(Class<T> type) {
    return new SimpleKey<T>(type, annotationStrategy);
  }

  /**
   * Returns a new key of the specified type with the same annotation as this
   * key.
   */
  Key<?> ofType(Type type) {
    return new SimpleKey<Object>(type, annotationStrategy);
  }

  /**
   * Returns true if this key has annotation attributes.
   * @return
   */
  boolean hasAttributes() {
    return annotationStrategy.hasAttributes();
  }

  /**
   * Returns this key without annotation attributes, i.e. with only the
   * annotation type.
   */
  Key<T> withoutAttributes() {
    return new SimpleKey<T>(typeLiteral, annotationStrategy.withoutAttributes());
  }

  private static class SimpleKey<T> extends Key<T> {

    private SimpleKey(Type type, AnnotationStrategy annotationStrategy) {
      super(type, annotationStrategy);
    }

    private SimpleKey(TypeLiteral<T> typeLiteral,
        AnnotationStrategy annotationStrategy) {
      super(typeLiteral, annotationStrategy);
    }
  }

  interface AnnotationStrategy {

    Annotation getAnnotation();
    Class<? extends Annotation> getAnnotationType();
    boolean hasAttributes();
    AnnotationStrategy withoutAttributes();
  }

  static final AnnotationStrategy NULL_STRATEGY = new AnnotationStrategy() {

    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;
    }

    public boolean equals(Object o) {
      return o == NULL_STRATEGY;
    }

    public int hashCode() {
      return 0;
    }

    public String toString() {
      return "[none]";
    }
  };

  /**
   * 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) {
    nonNull(annotation, "annotation");
    Class<? extends Annotation> annotationType = annotation.annotationType();
    ensureRetainedAtRuntime(annotationType);
    ensureIsBindingAnnotation(annotationType);
    return new AnnotationInstanceStrategy(annotation);
  }

  /**
   * Gets the strategy for an annotation type.
   */
  static AnnotationStrategy strategyFor(
      Class<? extends Annotation> annotationType) {
    nonNull(annotationType, "annotation type");
    ensureRetainedAtRuntime(annotationType);
    ensureIsBindingAnnotation(annotationType);
    return new AnnotationTypeStrategy(annotationType, null);
  }

  private static void ensureRetainedAtRuntime(
      Class<? extends Annotation> annotationType) {
    if (!Annotations.isRetainedAtRuntime(annotationType)) {
      throw new IllegalArgumentException(annotationType.getName()
          + " is not retained at runtime."
          + " Please annotate it with @Retention(RUNTIME).");
    }
  }

  private static void ensureIsBindingAnnotation(
      Class<? extends Annotation> annotationType) {
    if (!isBindingAnnotation(annotationType)) {
      throw new IllegalArgumentException(annotationType.getName()
          + " is not a binding annotation."
          + " Please annotate it with @BindingAnnotation.");
    }
  }

  // this class not test-covered
  static class AnnotationInstanceStrategy implements AnnotationStrategy {

    final Annotation annotation;

    AnnotationInstanceStrategy(Annotation annotation) {
      this.annotation = nonNull(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();
    }

    public boolean equals(Object o) {
      if (!(o instanceof AnnotationInstanceStrategy)) {
        return false;
      }

      AnnotationInstanceStrategy other = (AnnotationInstanceStrategy) o;
      return annotation.equals(other.annotation);
    }

    public int hashCode() {
      return annotation.hashCode();
    }

    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 = nonNull(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;
    }

    public boolean equals(Object o) {
      if (!(o instanceof AnnotationTypeStrategy)) {
        return false;
      }

      AnnotationTypeStrategy other = (AnnotationTypeStrategy) o;
      return annotationType.equals(other.annotationType);
    }

    public int hashCode() {
      return annotationType.hashCode();
    }

    public String toString() {
      return "@" + annotationType.getName();
    }
  }

  static boolean isBindingAnnotation(Annotation annotation) {
    return isBindingAnnotation(annotation.annotationType());
  }

  static boolean isBindingAnnotation(
      Class<? extends Annotation> annotationType) {
    return annotationType.isAnnotationPresent(BindingAnnotation.class);
  }
}
TOP

Related Classes of com.google.inject.Key

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.