1 /* 2 * Copyright (C) 2019 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.hilt.processor.internal.definecomponent; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 22 23 import com.google.auto.common.MoreElements; 24 import com.google.auto.value.AutoValue; 25 import com.google.common.collect.ArrayListMultimap; 26 import com.google.common.collect.ImmutableList; 27 import com.google.common.collect.ListMultimap; 28 import com.squareup.javapoet.ClassName; 29 import dagger.hilt.processor.internal.AnnotationValues; 30 import dagger.hilt.processor.internal.ClassNames; 31 import dagger.hilt.processor.internal.ComponentDescriptor; 32 import dagger.hilt.processor.internal.ComponentTree; 33 import dagger.hilt.processor.internal.ProcessorErrors; 34 import dagger.hilt.processor.internal.Processors; 35 import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata; 36 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata; 37 import java.util.HashMap; 38 import java.util.LinkedHashMap; 39 import java.util.Map; 40 import java.util.Optional; 41 import javax.lang.model.element.AnnotationMirror; 42 import javax.lang.model.element.AnnotationValue; 43 import javax.lang.model.element.Element; 44 import javax.lang.model.element.PackageElement; 45 import javax.lang.model.element.TypeElement; 46 import javax.lang.model.util.Elements; 47 48 /** 49 * A utility class for getting {@link DefineComponentMetadata} and {@link 50 * DefineComponentBuilderMetadata}. 51 */ 52 public final class DefineComponents { 53 static final String AGGREGATING_PACKAGE = 54 DefineComponents.class.getPackage().getName() + ".codegen"; 55 create()56 public static DefineComponents create() { 57 return new DefineComponents(); 58 } 59 60 private final Map<Element, ComponentDescriptor> componentDescriptors = new HashMap<>(); 61 private final DefineComponentMetadatas componentMetadatas = DefineComponentMetadatas.create(); 62 private final DefineComponentBuilderMetadatas componentBuilderMetadatas = 63 DefineComponentBuilderMetadatas.create(componentMetadatas); 64 DefineComponents()65 private DefineComponents() {} 66 67 /** Returns the {@link ComponentDescriptor} for the given component element. */ 68 // TODO(b/144940889): This descriptor doesn't contain the "creator" or the "installInName". componentDescriptor(Element element)69 public ComponentDescriptor componentDescriptor(Element element) { 70 if (!componentDescriptors.containsKey(element)) { 71 componentDescriptors.put(element, uncachedComponentDescriptor(element)); 72 } 73 return componentDescriptors.get(element); 74 } 75 uncachedComponentDescriptor(Element element)76 private ComponentDescriptor uncachedComponentDescriptor(Element element) { 77 DefineComponentMetadata metadata = componentMetadatas.get(element); 78 ComponentDescriptor.Builder builder = 79 ComponentDescriptor.builder() 80 .component(ClassName.get(metadata.component())) 81 .scopes(metadata.scopes().stream().map(ClassName::get).collect(toImmutableSet())); 82 83 84 metadata.parentMetadata() 85 .map(DefineComponentMetadata::component) 86 .map(this::componentDescriptor) 87 .ifPresent(builder::parent); 88 89 return builder.build(); 90 } 91 92 /** Returns the {@link ComponentTree} from the aggregated {@link ComponentDescriptor}s. */ getComponentTree(Elements elements)93 public ComponentTree getComponentTree(Elements elements) { 94 AggregatedMetadata aggregatedMetadata = 95 AggregatedMetadata.from(elements, componentMetadatas, componentBuilderMetadatas); 96 ListMultimap<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMultimap = 97 ArrayListMultimap.create(); 98 aggregatedMetadata.builders() 99 .forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder)); 100 101 // Check that there are not multiple builders per component 102 for (DefineComponentMetadata componentMetadata : builderMultimap.keySet()) { 103 TypeElement component = componentMetadata.component(); 104 ProcessorErrors.checkState( 105 builderMultimap.get(componentMetadata).size() <= 1, 106 component, 107 "Multiple @%s declarations are not allowed for @%s type, %s. Found: %s", 108 ClassNames.DEFINE_COMPONENT_BUILDER, 109 ClassNames.DEFINE_COMPONENT, 110 component, 111 builderMultimap.get(componentMetadata).stream() 112 .map(DefineComponentBuilderMetadata::builder) 113 .map(TypeElement::toString) 114 .sorted() 115 .collect(toImmutableList())); 116 } 117 118 // Now that we know there is at most 1 builder per component, convert the Multimap to Map. 119 Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap = new LinkedHashMap<>(); 120 builderMultimap.entries().forEach(e -> builderMap.put(e.getKey(), e.getValue())); 121 122 123 return ComponentTree.from(aggregatedMetadata.components().stream() 124 .map(componentMetadata -> toComponentDescriptor(componentMetadata, builderMap)) 125 .collect(toImmutableSet())); 126 } 127 toComponentDescriptor( DefineComponentMetadata componentMetadata, Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap)128 private static ComponentDescriptor toComponentDescriptor( 129 DefineComponentMetadata componentMetadata, 130 Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap) { 131 ComponentDescriptor.Builder builder = 132 ComponentDescriptor.builder() 133 .component(ClassName.get(componentMetadata.component())) 134 .scopes( 135 componentMetadata.scopes().stream().map(ClassName::get).collect(toImmutableSet())); 136 137 138 if (builderMap.containsKey(componentMetadata)) { 139 builder.creator(ClassName.get(builderMap.get(componentMetadata).builder())); 140 } 141 142 componentMetadata 143 .parentMetadata() 144 .map(parent -> toComponentDescriptor(parent, builderMap)) 145 .ifPresent(builder::parent); 146 147 return builder.build(); 148 } 149 150 @AutoValue 151 abstract static class AggregatedMetadata { 152 /** Returns the aggregated metadata for {@link DefineComponentClasses#component()}. */ components()153 abstract ImmutableList<DefineComponentMetadata> components(); 154 155 /** Returns the aggregated metadata for {@link DefineComponentClasses#builder()}. */ builders()156 abstract ImmutableList<DefineComponentBuilderMetadata> builders(); 157 from( Elements elements, DefineComponentMetadatas componentMetadatas, DefineComponentBuilderMetadatas componentBuilderMetadatas)158 static AggregatedMetadata from( 159 Elements elements, 160 DefineComponentMetadatas componentMetadatas, 161 DefineComponentBuilderMetadatas componentBuilderMetadatas) { 162 PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE); 163 164 if (packageElement == null) { 165 return new AutoValue_DefineComponents_AggregatedMetadata( 166 ImmutableList.of(), ImmutableList.of()); 167 } 168 169 ImmutableList.Builder<DefineComponentMetadata> components = ImmutableList.builder(); 170 ImmutableList.Builder<DefineComponentBuilderMetadata> builders = ImmutableList.builder(); 171 for (Element element : packageElement.getEnclosedElements()) { 172 ProcessorErrors.checkState( 173 MoreElements.isType(element), 174 element, 175 "Only types may be in package %s. Did you add custom code in the package?", 176 packageElement); 177 178 TypeElement typeElement = MoreElements.asType(element); 179 ProcessorErrors.checkState( 180 Processors.hasAnnotation(typeElement, ClassNames.DEFINE_COMPONENT_CLASSES), 181 typeElement, 182 "Class, %s, must be annotated with @%s. Found: %s.", 183 typeElement, 184 ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(), 185 typeElement.getAnnotationMirrors()); 186 187 Optional<TypeElement> component = defineComponentClass(elements, typeElement, "component"); 188 Optional<TypeElement> builder = defineComponentClass(elements, typeElement, "builder"); 189 ProcessorErrors.checkState( 190 component.isPresent() || builder.isPresent(), 191 typeElement, 192 "@DefineComponentClasses missing both `component` and `builder` members."); 193 194 component.map(componentMetadatas::get).ifPresent(components::add); 195 builder.map(componentBuilderMetadatas::get).ifPresent(builders::add); 196 } 197 198 return new AutoValue_DefineComponents_AggregatedMetadata( 199 components.build(), builders.build()); 200 } 201 defineComponentClass( Elements elements, Element element, String annotationMember)202 private static Optional<TypeElement> defineComponentClass( 203 Elements elements, Element element, String annotationMember) { 204 AnnotationMirror mirror = 205 Processors.getAnnotationMirror(element, ClassNames.DEFINE_COMPONENT_CLASSES); 206 AnnotationValue value = getAnnotationElementAndValue(mirror, annotationMember).getValue(); 207 String className = AnnotationValues.getString(value); 208 209 if (className.isEmpty()) { // The default value. 210 return Optional.empty(); 211 } 212 213 TypeElement type = elements.getTypeElement(className); 214 ProcessorErrors.checkState( 215 type != null, 216 element, 217 "%s.%s(), has invalid value: `%s`.", 218 ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(), 219 annotationMember, 220 className); 221 222 return Optional.of(type); 223 } 224 } 225 } 226