• 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.getAnnotatedAnnotations;
20 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
21 import static com.google.auto.common.MoreElements.getAnnotationMirror;
22 import static com.google.auto.common.MoreElements.isAnnotationPresent;
23 import static java.util.stream.Collectors.toList;
24 import static javax.lang.model.element.Modifier.ABSTRACT;
25 
26 import com.google.auto.common.MoreElements;
27 import com.google.auto.common.MoreTypes;
28 import com.google.auto.value.AutoValue;
29 import com.google.common.collect.ImmutableSet;
30 import com.squareup.javapoet.AnnotationSpec;
31 import com.squareup.javapoet.ClassName;
32 import com.squareup.javapoet.TypeName;
33 import dagger.Module;
34 import dagger.android.ContributesAndroidInjector;
35 import java.util.List;
36 import java.util.Optional;
37 import javax.annotation.processing.Messager;
38 import javax.inject.Qualifier;
39 import javax.inject.Scope;
40 import javax.lang.model.element.AnnotationMirror;
41 import javax.lang.model.element.AnnotationValue;
42 import javax.lang.model.element.Element;
43 import javax.lang.model.element.ExecutableElement;
44 import javax.lang.model.element.TypeElement;
45 import javax.lang.model.type.TypeMirror;
46 import javax.lang.model.util.SimpleAnnotationValueVisitor8;
47 import javax.tools.Diagnostic.Kind;
48 
49 /**
50  * A descriptor of a generated {@link Module} and {@link dagger.Subcomponent} to be generated from a
51  * {@link ContributesAndroidInjector} method.
52  */
53 @AutoValue
54 abstract class AndroidInjectorDescriptor {
55   /** The type to be injected; the return type of the {@link ContributesAndroidInjector} method. */
injectedType()56   abstract ClassName injectedType();
57 
58   /** Scopes to apply to the generated {@link dagger.Subcomponent}. */
scopes()59   abstract ImmutableSet<AnnotationSpec> scopes();
60 
61   /** @see ContributesAndroidInjector#modules() */
modules()62   abstract ImmutableSet<ClassName> modules();
63 
64   /** The {@link Module} that contains the {@link ContributesAndroidInjector} method. */
enclosingModule()65   abstract ClassName enclosingModule();
66 
67   /** The method annotated with {@link ContributesAndroidInjector}. */
method()68   abstract ExecutableElement method();
69 
70   @AutoValue.Builder
71   abstract static class Builder {
injectedType(ClassName injectedType)72     abstract Builder injectedType(ClassName injectedType);
73 
scopesBuilder()74     abstract ImmutableSet.Builder<AnnotationSpec> scopesBuilder();
75 
modulesBuilder()76     abstract ImmutableSet.Builder<ClassName> modulesBuilder();
77 
enclosingModule(ClassName enclosingModule)78     abstract Builder enclosingModule(ClassName enclosingModule);
79 
method(ExecutableElement method)80     abstract Builder method(ExecutableElement method);
81 
build()82     abstract AndroidInjectorDescriptor build();
83   }
84 
85   static final class Validator {
86     private final Messager messager;
87 
Validator(Messager messager)88     Validator(Messager messager) {
89       this.messager = messager;
90     }
91 
92     /**
93      * Validates a {@link ContributesAndroidInjector} method, returning an {@link
94      * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
95      */
createIfValid(ExecutableElement method)96     Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
97       ErrorReporter reporter = new ErrorReporter(method, messager);
98 
99       if (!method.getModifiers().contains(ABSTRACT)) {
100         reporter.reportError("@ContributesAndroidInjector methods must be abstract");
101       }
102 
103       if (!method.getParameters().isEmpty()) {
104         reporter.reportError("@ContributesAndroidInjector methods cannot have parameters");
105       }
106 
107       AndroidInjectorDescriptor.Builder builder =
108           new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
109       TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
110       if (!isAnnotationPresent(enclosingElement, Module.class)) {
111         reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
112       }
113       builder.enclosingModule(ClassName.get(enclosingElement));
114 
115       TypeMirror injectedType = method.getReturnType();
116       if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) {
117         builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType)));
118       } else {
119         reporter.reportError(
120             "@ContributesAndroidInjector methods cannot return parameterized types");
121       }
122 
123       AnnotationMirror annotation =
124           getAnnotationMirror(method, ContributesAndroidInjector.class).get();
125       for (TypeMirror module :
126           getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
127         if (isAnnotationPresent(MoreTypes.asElement(module), Module.class)) {
128           builder.modulesBuilder().add((ClassName) TypeName.get(module));
129         } else {
130           reporter.reportError(String.format("%s is not a @Module", module), annotation);
131         }
132       }
133 
134       for (AnnotationMirror scope : getAnnotatedAnnotations(method, Scope.class)) {
135         builder.scopesBuilder().add(AnnotationSpec.get(scope));
136       }
137 
138       for (AnnotationMirror qualifier : getAnnotatedAnnotations(method, Qualifier.class)) {
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