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