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 InjectProcessingStep( Messager messager, InjectConstructorValidator constructorValidator, InjectFieldValidator fieldValidator, InjectMethodValidator methodValidator, ProvisionBinding.Factory provisionBindingFactory, MembersInjectionBinding.Factory membersInjectionBindingFactory, InjectBindingRegistry factoryRegistrar)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 annotations()71 public Set<Class<? extends Annotation>> annotations() { 72 return ImmutableSet.<Class<? extends Annotation>>of(Inject.class); 73 } 74 75 @Override process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)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