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 
Scope(@ullable AnnotationMirror annotationMirror)49   private Scope(@Nullable AnnotationMirror annotationMirror) {
50     this.annotationMirror = annotationMirror;
51   }
52 
Scope()53   private Scope() {
54     this(null);
55   }
56 
57   /**
58    * Returns representation for an unscoped binding.
59    */
unscoped()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    */
scopeOf(Element element)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    */
isPresent()76   public boolean isPresent() {
77     return annotationMirror != null;
78   }
79 
80   /**
81    * Returns true if the scope represents the {@link Singleton @Singleton} annotation.
82    */
isSingleton()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    */
getReadableSource()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    */
getQualifiedName()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
equals(Object obj)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
hashCode()134   public int hashCode() {
135     return AnnotationMirrors.equivalence().hash(annotationMirror);
136   }
137 
138   /**
139    * Returns a debug representation of the scope.
140    */
141   @Override
toString()142   public String toString() {
143     return annotationMirror == null ? "UNSCOPED" : annotationMirror.toString();
144   }
145 }
146