Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2015 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 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.AnnotationMirrors;
     19 import com.google.auto.common.MoreTypes;
     20 import com.google.common.base.Optional;
     21 import com.google.common.base.Preconditions;
     22 import javax.annotation.Nullable;
     23 import javax.inject.Singleton;
     24 import javax.lang.model.element.AnnotationMirror;
     25 import javax.lang.model.element.Element;
     26 import javax.lang.model.element.TypeElement;
     27 
     28 import static com.google.auto.common.MoreTypes.isTypeOf;
     29 import static dagger.internal.codegen.ErrorMessages.stripCommonTypePrefixes;
     30 import static dagger.internal.codegen.InjectionAnnotations.getScopeAnnotation;
     31 
     32 /**
     33  * A representation of the scope (or lack of it) associated with a component, providing method
     34  * or injection location.
     35  */
     36 final class Scope {
     37 
     38   /**
     39    * An internal representation for an unscoped binding.
     40    */
     41   private static final Scope UNSCOPED = new Scope();
     42 
     43   /**
     44    * The underlying {@link AnnotationMirror} that represents the scope annotation.
     45    */
     46   @Nullable
     47   private final AnnotationMirror annotationMirror;
     48 
     49   private Scope(@Nullable AnnotationMirror annotationMirror) {
     50     this.annotationMirror = annotationMirror;
     51   }
     52 
     53   private Scope() {
     54     this(null);
     55   }
     56 
     57   /**
     58    * Returns representation for an unscoped binding.
     59    */
     60   static Scope unscoped() {
     61     return UNSCOPED;
     62   }
     63 
     64   /**
     65    * If the source code element has an associated scoped annotation then returns a representation
     66    * of that scope, otherwise returns a representation for an unscoped binding.
     67    */
     68   static Scope scopeOf(Element element) {
     69     Optional<AnnotationMirror> scopeAnnotation = getScopeAnnotation(element);
     70     return scopeAnnotation.isPresent() ? new Scope(scopeAnnotation.get()) : UNSCOPED;
     71   }
     72 
     73   /**
     74    * Returns true if the scope is present, i.e. it's not unscoped binding.
     75    */
     76   public boolean isPresent() {
     77     return annotationMirror != null;
     78   }
     79 
     80   /**
     81    * Returns true if the scope represents the {@link Singleton @Singleton} annotation.
     82    */
     83   public boolean isSingleton() {
     84     return annotationMirror != null
     85         && isTypeOf(Singleton.class, annotationMirror.getAnnotationType());
     86   }
     87 
     88   /**
     89    * Returns the readable source representation (name with @ prefix) of the annotation type.
     90    *
     91    * <p>It's readable source because it has had common package prefixes removed, e.g.
     92    * {@code @javax.inject.Singleton} is returned as {@code @Singleton}.
     93    *
     94    * <p>Make sure that the scope is actually {@link #isPresent() present} before calling as it will
     95    * throw an {@link IllegalStateException} otherwise. This does not return any annotation values
     96    * as according to {@link javax.inject.Scope} scope annotations are not supposed to use them.
     97    */
     98   public String getReadableSource() {
     99     return stripCommonTypePrefixes("@" + getQualifiedName());
    100   }
    101 
    102   /**
    103    * Returns the fully qualified name of the annotation type.
    104    *
    105    * <p>Make sure that the scope is actually {@link #isPresent() present} before calling as it will
    106    * throw an {@link IllegalStateException} otherwise. This does not return any annotation values
    107    * as according to {@link javax.inject.Scope} scope annotations are not supposed to use them.
    108    */
    109   public String getQualifiedName() {
    110     Preconditions.checkState(annotationMirror != null,
    111         "Cannot create a stripped source representation of no annotation");
    112     TypeElement typeElement = MoreTypes.asTypeElement(annotationMirror.getAnnotationType());
    113     return typeElement.getQualifiedName().toString();
    114   }
    115 
    116   /**
    117    * Scopes are equal if the underlying {@link AnnotationMirror} are equivalent according to
    118    * {@link AnnotationMirrors#equivalence()}.
    119    */
    120   @Override
    121   public boolean equals(Object obj) {
    122     if (this == obj) {
    123       return true;
    124     } else if (obj instanceof Scope) {
    125       Scope that = (Scope) obj;
    126       return AnnotationMirrors.equivalence()
    127         .equivalent(this.annotationMirror, that.annotationMirror);
    128     } else {
    129       return false;
    130     }
    131   }
    132 
    133   @Override
    134   public int hashCode() {
    135     return AnnotationMirrors.equivalence().hash(annotationMirror);
    136   }
    137 
    138   /**
    139    * Returns a debug representation of the scope.
    140    */
    141   @Override
    142   public String toString() {
    143     return annotationMirror == null ? "UNSCOPED" : annotationMirror.toString();
    144   }
    145 }
    146