1 /* 2 * Copyright (C) 2015 The Dagger Authors. 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 17 package dagger.internal.codegen; 18 19 import static com.google.common.base.Preconditions.checkState; 20 import static dagger.internal.codegen.ComponentGenerator.componentName; 21 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent; 22 import static javax.tools.Diagnostic.Kind.WARNING; 23 24 import com.squareup.javapoet.ClassName; 25 import dagger.internal.codegen.langmodel.DaggerElements; 26 import dagger.internal.codegen.serialization.ProtoSerialization.InconsistentSerializedProtoException; 27 import java.util.HashMap; 28 import java.util.Map; 29 import java.util.Optional; 30 import javax.annotation.processing.Messager; 31 import javax.inject.Inject; 32 import javax.inject.Singleton; 33 import javax.lang.model.element.TypeElement; 34 35 /** Factory for {@link ComponentImplementation}s. */ 36 @Singleton 37 final class ComponentImplementationFactory implements ClearableCache { 38 private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>(); 39 private final KeyFactory keyFactory; 40 private final CompilerOptions compilerOptions; 41 private final BindingGraphFactory bindingGraphFactory; 42 private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder; 43 private final DeserializedComponentImplementationBuilder 44 deserializedComponentImplementationBuilder; 45 private final DaggerElements elements; 46 private final Messager messager; 47 48 @Inject ComponentImplementationFactory( KeyFactory keyFactory, CompilerOptions compilerOptions, BindingGraphFactory bindingGraphFactory, TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder, DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder, DaggerElements elements, Messager messager)49 ComponentImplementationFactory( 50 KeyFactory keyFactory, 51 CompilerOptions compilerOptions, 52 BindingGraphFactory bindingGraphFactory, 53 TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder, 54 DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder, 55 DaggerElements elements, 56 Messager messager) { 57 this.keyFactory = keyFactory; 58 this.compilerOptions = compilerOptions; 59 this.bindingGraphFactory = bindingGraphFactory; 60 this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder; 61 this.deserializedComponentImplementationBuilder = deserializedComponentImplementationBuilder; 62 this.elements = elements; 63 this.messager = messager; 64 } 65 66 /** 67 * Returns a top-level (non-nested) component implementation for a binding graph. 68 * 69 * @throws IllegalStateException if the binding graph is for a subcomponent and 70 * ahead-of-time-subcomponents mode is not enabled 71 */ createComponentImplementation(BindingGraph bindingGraph)72 ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) { 73 return reentrantComputeIfAbsent( 74 topLevelComponentCache, 75 bindingGraph.componentTypeElement(), 76 component -> createComponentImplementationUncached(bindingGraph)); 77 } 78 createComponentImplementationUncached(BindingGraph bindingGraph)79 private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) { 80 ComponentImplementation componentImplementation = 81 ComponentImplementation.topLevelComponentImplementation( 82 bindingGraph, 83 componentName(bindingGraph.componentTypeElement()), 84 new SubcomponentNames(bindingGraph, keyFactory), 85 compilerOptions); 86 87 // TODO(dpb): explore using optional bindings for the "parent" bindings 88 CurrentImplementationSubcomponent currentImplementationSubcomponent = 89 topLevelImplementationComponentBuilder 90 .topLevelComponent(componentImplementation) 91 .build() 92 .currentImplementationSubcomponentBuilder() 93 .componentImplementation(componentImplementation) 94 .bindingGraph(bindingGraph) 95 .parentBuilder(Optional.empty()) 96 .parentBindingExpressions(Optional.empty()) 97 .parentRequirementExpressions(Optional.empty()) 98 .build(); 99 100 if (componentImplementation.isAbstract()) { 101 checkState( 102 compilerOptions.aheadOfTimeSubcomponents(), 103 "Calling 'componentImplementation()' on %s when not generating ahead-of-time " 104 + "subcomponents.", 105 bindingGraph.componentTypeElement()); 106 return currentImplementationSubcomponent.subcomponentBuilder().build(); 107 } else { 108 return currentImplementationSubcomponent.rootComponentBuilder().build(); 109 } 110 } 111 112 /** Returns the superclass of the child nested within a superclass of the parent component. */ findChildSuperclassImplementation( ComponentDescriptor child, ComponentImplementation parentImplementation)113 ComponentImplementation findChildSuperclassImplementation( 114 ComponentDescriptor child, ComponentImplementation parentImplementation) { 115 // If the current component has superclass implementations, a superclass may contain a 116 // reference to the child. Traverse this component's superimplementation hierarchy looking for 117 // the child's implementation. The child superclass implementation may not be present in the 118 // direct superclass implementations if the subcomponent builder was previously a pruned 119 // binding. 120 for (Optional<ComponentImplementation> parent = parentImplementation.superclassImplementation(); 121 parent.isPresent(); 122 parent = parent.get().superclassImplementation()) { 123 Optional<ComponentImplementation> superclass = parent.get().childImplementation(child); 124 if (superclass.isPresent()) { 125 return superclass.get(); 126 } 127 } 128 129 if (compilerOptions.emitModifiableMetadataAnnotations()) { 130 ClassName childSuperclassName = componentName(child.typeElement()); 131 TypeElement generatedChildSuperclassImplementation = 132 elements.getTypeElement(childSuperclassName); 133 if (generatedChildSuperclassImplementation != null) { 134 try { 135 return deserializedComponentImplementationBuilder.create( 136 child, generatedChildSuperclassImplementation); 137 } catch (InconsistentSerializedProtoException e) { 138 messager.printMessage( 139 WARNING, 140 String.format( 141 "%s was compiled with a different version of Dagger than the version in this " 142 + "compilation. To ensure the validity of Dagger's generated code, compile " 143 + "all Dagger code with the same version.", 144 child.typeElement().getQualifiedName())); 145 } 146 } else if (compilerOptions.forceUseSerializedComponentImplementations()) { 147 throw new TypeNotPresentException(childSuperclassName.toString(), null); 148 } 149 } 150 151 // Otherwise, the superclass implementation is top-level, so we must recreate the 152 // implementation object for the base implementation of the child by truncating the binding 153 // graph at the child. 154 BindingGraph truncatedBindingGraph = bindingGraphFactory.create(child, false); 155 return createComponentImplementation(truncatedBindingGraph); 156 } 157 158 @Override clearCache()159 public void clearCache() { 160 topLevelComponentCache.clear(); 161 } 162 } 163