1 /* 2 * Copyright (C) 2014 Google, Inc. 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 dagger.internal.codegen; 17 18 import com.google.auto.common.BasicAnnotationProcessor; 19 import com.google.auto.common.MoreElements; 20 import com.google.common.base.Function; 21 import com.google.common.collect.FluentIterable; 22 import com.google.common.collect.ImmutableSet; 23 import com.google.common.collect.SetMultimap; 24 import com.google.common.collect.Sets; 25 import dagger.Module; 26 import dagger.Provides; 27 import java.lang.annotation.Annotation; 28 import java.util.List; 29 import java.util.Set; 30 import javax.annotation.processing.Messager; 31 import javax.lang.model.element.Element; 32 import javax.lang.model.element.ExecutableElement; 33 import javax.lang.model.element.TypeElement; 34 import javax.lang.model.util.ElementFilter; 35 36 import static com.google.auto.common.MoreElements.isAnnotationPresent; 37 import static javax.lang.model.element.ElementKind.METHOD; 38 39 /** 40 * An annotation processor for generating Dagger implementation code based on the {@link Module} 41 * (and {@link Provides}) annotation. 42 * 43 * @author Gregory Kick 44 * @since 2.0 45 */ 46 final class ModuleProcessingStep implements BasicAnnotationProcessor.ProcessingStep { 47 private final Messager messager; 48 private final ModuleValidator moduleValidator; 49 private final ProvidesMethodValidator providesMethodValidator; 50 private final ProvisionBinding.Factory provisionBindingFactory; 51 private final FactoryGenerator factoryGenerator; 52 private final Set<Element> processedModuleElements = Sets.newLinkedHashSet(); 53 54 ModuleProcessingStep( 55 Messager messager, 56 ModuleValidator moduleValidator, 57 ProvidesMethodValidator providesMethodValidator, 58 ProvisionBinding.Factory provisionBindingFactory, 59 FactoryGenerator factoryGenerator) { 60 this.messager = messager; 61 this.moduleValidator = moduleValidator; 62 this.providesMethodValidator = providesMethodValidator; 63 this.provisionBindingFactory = provisionBindingFactory; 64 this.factoryGenerator = factoryGenerator; 65 } 66 67 @Override 68 public Set<Class<? extends Annotation>> annotations() { 69 return ImmutableSet.of(Module.class, Provides.class); 70 } 71 72 @Override 73 public Set<Element> process( 74 SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { 75 // first, check and collect all provides methods 76 ImmutableSet.Builder<ExecutableElement> validProvidesMethodsBuilder = ImmutableSet.builder(); 77 for (Element providesElement : elementsByAnnotation.get(Provides.class)) { 78 if (providesElement.getKind().equals(METHOD)) { 79 ExecutableElement providesMethodElement = (ExecutableElement) providesElement; 80 ValidationReport<ExecutableElement> methodReport = 81 providesMethodValidator.validate(providesMethodElement); 82 methodReport.printMessagesTo(messager); 83 if (methodReport.isClean()) { 84 validProvidesMethodsBuilder.add(providesMethodElement); 85 } 86 } 87 } 88 ImmutableSet<ExecutableElement> validProvidesMethods = validProvidesMethodsBuilder.build(); 89 90 // process each module 91 for (Element moduleElement : 92 Sets.difference(elementsByAnnotation.get(Module.class), processedModuleElements)) { 93 ValidationReport<TypeElement> report = 94 moduleValidator.validate(MoreElements.asType(moduleElement)); 95 report.printMessagesTo(messager); 96 97 if (report.isClean()) { 98 ImmutableSet.Builder<ExecutableElement> moduleProvidesMethodsBuilder = 99 ImmutableSet.builder(); 100 List<ExecutableElement> moduleMethods = 101 ElementFilter.methodsIn(moduleElement.getEnclosedElements()); 102 for (ExecutableElement methodElement : moduleMethods) { 103 if (isAnnotationPresent(methodElement, Provides.class)) { 104 moduleProvidesMethodsBuilder.add(methodElement); 105 } 106 } 107 ImmutableSet<ExecutableElement> moduleProvidesMethods = 108 moduleProvidesMethodsBuilder.build(); 109 110 if (Sets.difference(moduleProvidesMethods, validProvidesMethods).isEmpty()) { 111 // all of the provides methods in this module are valid! 112 // time to generate some factories! 113 ImmutableSet<ProvisionBinding> bindings = FluentIterable.from(moduleProvidesMethods) 114 .transform(new Function<ExecutableElement, ProvisionBinding>() { 115 @Override 116 public ProvisionBinding apply(ExecutableElement providesMethod) { 117 return provisionBindingFactory.forProvidesMethod(providesMethod, 118 providesMethod.getEnclosingElement().asType()); 119 } 120 }) 121 .toSet(); 122 123 try { 124 for (ProvisionBinding binding : bindings) { 125 factoryGenerator.generate(binding); 126 } 127 } catch (SourceFileGenerationException e) { 128 e.printMessageTo(messager); 129 } 130 } 131 } 132 processedModuleElements.add(moduleElement); 133 } 134 return ImmutableSet.of(); 135 } 136 } 137