• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Dagger Authors.
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 
17 package dagger.android.processor;
18 
19 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
20 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations;
21 import static java.util.stream.Collectors.toList;
22 import static javax.lang.model.element.Modifier.ABSTRACT;
23 
24 import com.google.auto.common.MoreElements;
25 import com.google.auto.common.MoreTypes;
26 import com.google.auto.value.AutoValue;
27 import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Sets;
29 import com.squareup.javapoet.AnnotationSpec;
30 import com.squareup.javapoet.ClassName;
31 import com.squareup.javapoet.TypeName;
32 import java.util.List;
33 import java.util.Optional;
34 import javax.annotation.processing.Messager;
35 import javax.lang.model.element.AnnotationMirror;
36 import javax.lang.model.element.AnnotationValue;
37 import javax.lang.model.element.Element;
38 import javax.lang.model.element.ExecutableElement;
39 import javax.lang.model.element.TypeElement;
40 import javax.lang.model.type.TypeMirror;
41 import javax.lang.model.util.SimpleAnnotationValueVisitor8;
42 import javax.tools.Diagnostic.Kind;
43 
44 /**
45  * A descriptor of a generated {@link dagger.Module} and {@link dagger.Subcomponent} to be generated
46  * from a {@code ContributesAndroidInjector} method.
47  */
48 @AutoValue
49 abstract class AndroidInjectorDescriptor {
50   /** The type to be injected; the return type of the {@code ContributesAndroidInjector} method. */
injectedType()51   abstract ClassName injectedType();
52 
53   /** Scopes to apply to the generated {@link dagger.Subcomponent}. */
scopes()54   abstract ImmutableSet<AnnotationSpec> scopes();
55 
56   /** See {@code ContributesAndroidInjector#modules()} */
modules()57   abstract ImmutableSet<ClassName> modules();
58 
59   /** The {@link dagger.Module} that contains the {@code ContributesAndroidInjector} method. */
enclosingModule()60   abstract ClassName enclosingModule();
61 
62   /** The method annotated with {@code ContributesAndroidInjector}. */
method()63   abstract ExecutableElement method();
64 
65   @AutoValue.Builder
66   abstract static class Builder {
injectedType(ClassName injectedType)67     abstract Builder injectedType(ClassName injectedType);
68 
scopesBuilder()69     abstract ImmutableSet.Builder<AnnotationSpec> scopesBuilder();
70 
modulesBuilder()71     abstract ImmutableSet.Builder<ClassName> modulesBuilder();
72 
enclosingModule(ClassName enclosingModule)73     abstract Builder enclosingModule(ClassName enclosingModule);
74 
method(ExecutableElement method)75     abstract Builder method(ExecutableElement method);
76 
build()77     abstract AndroidInjectorDescriptor build();
78   }
79 
80   static final class Validator {
81     private final Messager messager;
82 
Validator(Messager messager)83     Validator(Messager messager) {
84       this.messager = messager;
85     }
86 
87     /**
88      * Validates a {@code ContributesAndroidInjector} method, returning an {@link
89      * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
90      */
createIfValid(ExecutableElement method)91     Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
92       ErrorReporter reporter = new ErrorReporter(method, messager);
93 
94       if (!method.getModifiers().contains(ABSTRACT)) {
95         reporter.reportError("@ContributesAndroidInjector methods must be abstract");
96       }
97 
98       if (!method.getParameters().isEmpty()) {
99         reporter.reportError("@ContributesAndroidInjector methods cannot have parameters");
100       }
101 
102       AndroidInjectorDescriptor.Builder builder =
103           new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
104       TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
105       if (!MoreDaggerElements.isAnnotationPresent(enclosingElement, TypeNames.MODULE)) {
106         reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
107       }
108       builder.enclosingModule(ClassName.get(enclosingElement));
109 
110       TypeMirror injectedType = method.getReturnType();
111       if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) {
112         builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType)));
113       } else {
114         reporter.reportError(
115             "@ContributesAndroidInjector methods cannot return parameterized types");
116       }
117 
118       AnnotationMirror annotation =
119           MoreDaggerElements.getAnnotationMirror(method, TypeNames.CONTRIBUTES_ANDROID_INJECTOR)
120               .get();
121       for (TypeMirror module :
122           getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
123         if (MoreDaggerElements.isAnnotationPresent(MoreTypes.asElement(module), TypeNames.MODULE)) {
124           builder.modulesBuilder().add((ClassName) TypeName.get(module));
125         } else {
126           reporter.reportError(String.format("%s is not a @Module", module), annotation);
127         }
128       }
129 
130       for (AnnotationMirror scope : Sets.union(
131           getAnnotatedAnnotations(method, TypeNames.SCOPE),
132           getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) {
133         builder.scopesBuilder().add(AnnotationSpec.get(scope));
134       }
135 
136       for (AnnotationMirror qualifier : Sets.union(
137           getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
138           getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) {
139         reporter.reportError(
140             "@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
141       }
142 
143       return reporter.hasError ? Optional.empty() : Optional.of(builder.build());
144     }
145 
146     // TODO(ronshapiro): use ValidationReport once it is moved out of the compiler
147     private static class ErrorReporter {
148       private final Element subject;
149       private final Messager messager;
150       private boolean hasError;
151 
ErrorReporter(Element subject, Messager messager)152       ErrorReporter(Element subject, Messager messager) {
153         this.subject = subject;
154         this.messager = messager;
155       }
156 
reportError(String error)157       void reportError(String error) {
158         hasError = true;
159         messager.printMessage(Kind.ERROR, error, subject);
160       }
161 
reportError(String error, AnnotationMirror annotation)162       void reportError(String error, AnnotationMirror annotation) {
163         hasError = true;
164         messager.printMessage(Kind.ERROR, error, subject, annotation);
165       }
166     }
167   }
168 
169   private static final class AllTypesVisitor
170       extends SimpleAnnotationValueVisitor8<ImmutableSet<TypeMirror>, Void> {
171     @Override
visitArray(List<? extends AnnotationValue> values, Void aVoid)172     public ImmutableSet<TypeMirror> visitArray(List<? extends AnnotationValue> values, Void aVoid) {
173       return ImmutableSet.copyOf(
174           values.stream().flatMap(v -> v.accept(this, null).stream()).collect(toList()));
175     }
176 
177     @Override
visitType(TypeMirror a, Void aVoid)178     public ImmutableSet<TypeMirror> visitType(TypeMirror a, Void aVoid) {
179       return ImmutableSet.of(a);
180     }
181 
182     @Override
defaultAction(Object o, Void aVoid)183     protected ImmutableSet<TypeMirror> defaultAction(Object o, Void aVoid) {
184       throw new AssertionError(o);
185     }
186   }
187 }
188