1 /*
2  * Copyright 2013 Google LLC
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 com.google.auto.factory.processor;
17 
18 import com.google.auto.common.MoreTypes;
19 import com.google.auto.factory.AutoFactory;
20 import com.google.auto.factory.Provided;
21 import com.google.auto.service.AutoService;
22 import com.google.common.base.Optional;
23 import com.google.common.base.Throwables;
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.ImmutableListMultimap;
26 import com.google.common.collect.ImmutableSet;
27 import com.google.common.collect.ImmutableSetMultimap;
28 import com.google.common.collect.ImmutableSortedSet;
29 import com.google.common.collect.Iterables;
30 import java.io.IOException;
31 import java.util.Arrays;
32 import java.util.Comparator;
33 import java.util.List;
34 import java.util.Set;
35 import javax.annotation.processing.AbstractProcessor;
36 import javax.annotation.processing.Messager;
37 import javax.annotation.processing.ProcessingEnvironment;
38 import javax.annotation.processing.Processor;
39 import javax.annotation.processing.RoundEnvironment;
40 import javax.lang.model.SourceVersion;
41 import javax.lang.model.element.Element;
42 import javax.lang.model.element.ExecutableElement;
43 import javax.lang.model.element.Modifier;
44 import javax.lang.model.element.TypeElement;
45 import javax.lang.model.type.ExecutableType;
46 import javax.lang.model.type.TypeMirror;
47 import javax.lang.model.util.ElementFilter;
48 import javax.lang.model.util.Elements;
49 import javax.lang.model.util.Types;
50 import javax.tools.Diagnostic.Kind;
51 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
52 import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
53 
54 /**
55  * The annotation processor that generates factories for {@link AutoFactory} annotations.
56  *
57  * @author Gregory Kick
58  */
59 @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
60 @AutoService(Processor.class)
61 public final class AutoFactoryProcessor extends AbstractProcessor {
62   private FactoryDescriptorGenerator factoryDescriptorGenerator;
63   private AutoFactoryDeclaration.Factory declarationFactory;
64   private ProvidedChecker providedChecker;
65   private Messager messager;
66   private Elements elements;
67   private Types types;
68 
69   @Override
init(ProcessingEnvironment processingEnv)70   public synchronized void init(ProcessingEnvironment processingEnv) {
71     super.init(processingEnv);
72     elements = processingEnv.getElementUtils();
73     types = processingEnv.getTypeUtils();
74     messager = processingEnv.getMessager();
75     providedChecker = new ProvidedChecker(messager);
76     declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager);
77     factoryDescriptorGenerator =
78         new FactoryDescriptorGenerator(messager, types, declarationFactory);
79   }
80 
81   @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)82   public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
83     try {
84       doProcess(roundEnv);
85     } catch (Throwable e) {
86       messager.printMessage(Kind.ERROR, "Failed to process @AutoFactory annotations:\n"
87           + Throwables.getStackTraceAsString(e));
88     }
89     return false;
90   }
91 
doProcess(RoundEnvironment roundEnv)92   private void doProcess(RoundEnvironment roundEnv) {
93     for (Element element : roundEnv.getElementsAnnotatedWith(Provided.class)) {
94       providedChecker.checkProvidedParameter(element);
95     }
96 
97     ImmutableListMultimap.Builder<PackageAndClass, FactoryMethodDescriptor> indexedMethodsBuilder =
98         ImmutableListMultimap.builder();
99     ImmutableSetMultimap.Builder<PackageAndClass, ImplementationMethodDescriptor>
100         implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder();
101     // Iterate over the classes and methods that are annotated with @AutoFactory.
102     for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) {
103       Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element);
104       if (declaration.isPresent()) {
105         PackageAndClass factoryName = declaration.get().getFactoryName();
106         TypeElement extendingType = declaration.get().extendingType();
107         implementationMethodDescriptorsBuilder.putAll(
108             factoryName, implementationMethods(extendingType, element));
109         for (TypeElement implementingType : declaration.get().implementingTypes()) {
110           implementationMethodDescriptorsBuilder.putAll(
111               factoryName, implementationMethods(implementingType, element));
112         }
113       }
114 
115       ImmutableSet<FactoryMethodDescriptor> descriptors =
116           factoryDescriptorGenerator.generateDescriptor(element);
117       for (FactoryMethodDescriptor descriptor : descriptors) {
118         indexedMethodsBuilder.put(descriptor.factoryName(), descriptor);
119       }
120     }
121 
122     ImmutableSetMultimap<PackageAndClass, ImplementationMethodDescriptor>
123         implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build();
124     ImmutableListMultimap<PackageAndClass, FactoryMethodDescriptor> indexedMethods =
125         indexedMethodsBuilder.build();
126     ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated =
127         simpleNamesToNames(indexedMethods.keySet());
128     FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated);
129 
130     indexedMethods.asMap().forEach(
131         (factoryName, methodDescriptors) -> {
132           // The sets of classes that are mentioned in the `extending` and `implementing` elements,
133           // respectively, of the @AutoFactory annotations for this factory.
134           ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder();
135           ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder();
136           boolean publicType = false;
137           Boolean allowSubclasses = null;
138           boolean skipCreation = false;
139           for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) {
140             extending.add(methodDescriptor.declaration().extendingType().asType());
141             for (TypeElement implementingType :
142                 methodDescriptor.declaration().implementingTypes()) {
143               implementing.add(implementingType.asType());
144             }
145             publicType |= methodDescriptor.publicMethod();
146             if (allowSubclasses == null) {
147               allowSubclasses = methodDescriptor.declaration().allowSubclasses();
148             } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) {
149               skipCreation = true;
150               messager.printMessage(Kind.ERROR,
151                   "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.",
152                   methodDescriptor.declaration().target(),
153                   methodDescriptor.declaration().mirror(),
154                   methodDescriptor.declaration().valuesMap().get("allowSubclasses"));
155             }
156           }
157           if (!skipCreation) {
158             try {
159               factoryWriter.writeFactory(
160                   FactoryDescriptor.create(
161                       factoryName,
162                       Iterables.getOnlyElement(extending.build()),
163                       implementing.build(),
164                       publicType,
165                       ImmutableSet.copyOf(methodDescriptors),
166                       implementationMethodDescriptors.get(factoryName),
167                       allowSubclasses));
168             } catch (IOException e) {
169               messager.printMessage(Kind.ERROR, "failed: " + e);
170             }
171           }
172         });
173   }
174 
implementationMethods( TypeElement supertype, Element autoFactoryElement)175   private ImmutableSet<ImplementationMethodDescriptor> implementationMethods(
176       TypeElement supertype, Element autoFactoryElement) {
177     ImmutableSet.Builder<ImplementationMethodDescriptor> implementationMethodsBuilder =
178         ImmutableSet.builder();
179     for (ExecutableElement implementationMethod :
180         ElementFilter.methodsIn(elements.getAllMembers(supertype))) {
181       if (implementationMethod.getModifiers().contains(Modifier.ABSTRACT)) {
182         ExecutableType methodType =
183             Elements2.getExecutableElementAsMemberOf(
184                 types, implementationMethod, supertype);
185         ImmutableSet<Parameter> passedParameters =
186             Parameter.forParameterList(
187                 implementationMethod.getParameters(), methodType.getParameterTypes(), types);
188         implementationMethodsBuilder.add(
189             ImplementationMethodDescriptor.builder()
190                 .name(implementationMethod.getSimpleName().toString())
191                 .returnType(getAnnotatedType(autoFactoryElement))
192                 .publicMethod()
193                 .passedParameters(passedParameters)
194                 .isVarArgs(implementationMethod.isVarArgs())
195                 .build());
196       }
197     }
198     return implementationMethodsBuilder.build();
199   }
200 
getAnnotatedType(Element element)201   private TypeMirror getAnnotatedType(Element element) {
202     List<TypeElement> types = ImmutableList.of();
203     while (types.isEmpty()) {
204       types = ElementFilter.typesIn(Arrays.asList(element));
205       element = element.getEnclosingElement();
206     }
207     return Iterables.getOnlyElement(types).asType();
208   }
209 
simpleNamesToNames( ImmutableSet<PackageAndClass> names)210   private static ImmutableSetMultimap<String, PackageAndClass> simpleNamesToNames(
211       ImmutableSet<PackageAndClass> names) {
212     // .collect(toImmutableSetMultimap(...)) would make this much simpler but ran into problems in
213     // Google's internal build system because of multiple Guava versions.
214     ImmutableSetMultimap.Builder<String, PackageAndClass> builder = ImmutableSetMultimap.builder();
215     for (PackageAndClass name : names) {
216       builder.put(name.className(), name);
217     }
218     return builder.build();
219   }
220 
newTypeSetBuilder()221   private static ImmutableSortedSet.Builder<TypeMirror> newTypeSetBuilder() {
222     return ImmutableSortedSet.orderedBy(
223         Comparator.comparing(t -> MoreTypes.asTypeElement(t).getQualifiedName().toString()));
224   }
225 
226   @Override
getSupportedAnnotationTypes()227   public ImmutableSet<String> getSupportedAnnotationTypes() {
228     return ImmutableSet.of(AutoFactory.class.getName(), Provided.class.getName());
229   }
230 
231   @Override
getSupportedSourceVersion()232   public SourceVersion getSupportedSourceVersion() {
233     return SourceVersion.latestSupported();
234   }
235 }
236