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