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.MoreTypes; 20 import com.google.common.base.Optional; 21 import com.google.common.collect.ImmutableSet; 22 import com.google.common.collect.SetMultimap; 23 24 import java.lang.annotation.Annotation; 25 import java.util.Set; 26 27 import javax.annotation.processing.Messager; 28 import javax.inject.Inject; 29 import javax.lang.model.element.Element; 30 import javax.lang.model.element.ExecutableElement; 31 import javax.lang.model.element.TypeElement; 32 import javax.lang.model.element.VariableElement; 33 import javax.lang.model.type.DeclaredType; 34 import javax.lang.model.type.TypeMirror; 35 import javax.lang.model.util.ElementKindVisitor6; 36 37 /** 38 * An annotation processor for generating Dagger implementation code based on the {@link Inject} 39 * annotation. 40 * 41 * @author Gregory Kick 42 * @since 2.0 43 */ 44 final class InjectProcessingStep implements BasicAnnotationProcessor.ProcessingStep { 45 private final Messager messager; 46 private final InjectConstructorValidator constructorValidator; 47 private final InjectFieldValidator fieldValidator; 48 private final InjectMethodValidator methodValidator; 49 private final ProvisionBinding.Factory provisionBindingFactory; 50 private final MembersInjectionBinding.Factory membersInjectionBindingFactory; 51 private final InjectBindingRegistry injectBindingRegistry; 52 53 InjectProcessingStep( 54 Messager messager, 55 InjectConstructorValidator constructorValidator, 56 InjectFieldValidator fieldValidator, 57 InjectMethodValidator methodValidator, 58 ProvisionBinding.Factory provisionBindingFactory, 59 MembersInjectionBinding.Factory membersInjectionBindingFactory, 60 InjectBindingRegistry factoryRegistrar) { 61 this.messager = messager; 62 this.constructorValidator = constructorValidator; 63 this.fieldValidator = fieldValidator; 64 this.methodValidator = methodValidator; 65 this.provisionBindingFactory = provisionBindingFactory; 66 this.membersInjectionBindingFactory = membersInjectionBindingFactory; 67 this.injectBindingRegistry = factoryRegistrar; 68 } 69 70 @Override 71 public Set<Class<? extends Annotation>> annotations() { 72 return ImmutableSet.<Class<? extends Annotation>>of(Inject.class); 73 } 74 75 @Override 76 public Set<Element> process( 77 SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { 78 ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder(); 79 // TODO(gak): add some error handling for bad source files 80 final ImmutableSet.Builder<ProvisionBinding> provisions = ImmutableSet.builder(); 81 // TODO(gak): instead, we should collect reports by type and check later 82 final ImmutableSet.Builder<DeclaredType> membersInjectedTypes = ImmutableSet.builder(); 83 84 for (Element injectElement : elementsByAnnotation.get(Inject.class)) { 85 try { 86 injectElement.accept( 87 new ElementKindVisitor6<Void, Void>() { 88 @Override 89 public Void visitExecutableAsConstructor( 90 ExecutableElement constructorElement, Void v) { 91 ValidationReport<TypeElement> report = 92 constructorValidator.validate(constructorElement); 93 94 report.printMessagesTo(messager); 95 96 if (report.isClean()) { 97 provisions.add( 98 provisionBindingFactory.forInjectConstructor( 99 constructorElement, Optional.<TypeMirror>absent())); 100 DeclaredType type = 101 MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType()); 102 if (membersInjectionBindingFactory.hasInjectedMembers(type)) { 103 membersInjectedTypes.add(type); 104 } 105 } 106 107 return null; 108 } 109 110 @Override 111 public Void visitVariableAsField(VariableElement fieldElement, Void p) { 112 ValidationReport<VariableElement> report = fieldValidator.validate(fieldElement); 113 114 report.printMessagesTo(messager); 115 116 if (report.isClean()) { 117 membersInjectedTypes.add( 118 MoreTypes.asDeclared(fieldElement.getEnclosingElement().asType())); 119 } 120 121 return null; 122 } 123 124 @Override 125 public Void visitExecutableAsMethod(ExecutableElement methodElement, Void p) { 126 ValidationReport<ExecutableElement> report = 127 methodValidator.validate(methodElement); 128 129 report.printMessagesTo(messager); 130 131 if (report.isClean()) { 132 membersInjectedTypes.add( 133 MoreTypes.asDeclared(methodElement.getEnclosingElement().asType())); 134 } 135 136 return null; 137 } 138 }, 139 null); 140 } catch (TypeNotPresentException e) { 141 rejectedElements.add(injectElement); 142 } 143 } 144 145 for (DeclaredType injectedType : membersInjectedTypes.build()) { 146 injectBindingRegistry.registerBinding(membersInjectionBindingFactory.forInjectedType( 147 injectedType, Optional.<TypeMirror>absent())); 148 } 149 150 for (ProvisionBinding binding : provisions.build()) { 151 injectBindingRegistry.registerBinding(binding); 152 } 153 return rejectedElements.build(); 154 } 155 } 156