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