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 dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 21 22 import com.google.common.collect.ArrayListMultimap; 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.ListMultimap; 25 import com.squareup.javapoet.ClassName; 26 import dagger.hilt.processor.internal.ClassNames; 27 import dagger.hilt.processor.internal.ComponentDescriptor; 28 import dagger.hilt.processor.internal.ProcessorErrors; 29 import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata; 30 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata; 31 import java.util.HashMap; 32 import java.util.LinkedHashMap; 33 import java.util.Map; 34 import javax.lang.model.element.Element; 35 import javax.lang.model.element.TypeElement; 36 import javax.lang.model.util.Elements; 37 38 /** 39 * A utility class for getting {@link DefineComponentMetadata} and {@link 40 * DefineComponentBuilderMetadata}. 41 */ 42 public final class DefineComponents { 43 create()44 public static DefineComponents create() { 45 return new DefineComponents(); 46 } 47 48 private final Map<Element, ComponentDescriptor> componentDescriptors = new HashMap<>(); 49 private final DefineComponentMetadatas componentMetadatas = DefineComponentMetadatas.create(); 50 private final DefineComponentBuilderMetadatas componentBuilderMetadatas = 51 DefineComponentBuilderMetadatas.create(componentMetadatas); 52 DefineComponents()53 private DefineComponents() {} 54 55 /** Returns the {@link ComponentDescriptor} for the given component element. */ 56 // TODO(b/144940889): This descriptor doesn't contain the "creator" or the "installInName". componentDescriptor(Element element)57 public ComponentDescriptor componentDescriptor(Element element) { 58 if (!componentDescriptors.containsKey(element)) { 59 componentDescriptors.put(element, uncachedComponentDescriptor(element)); 60 } 61 return componentDescriptors.get(element); 62 } 63 uncachedComponentDescriptor(Element element)64 private ComponentDescriptor uncachedComponentDescriptor(Element element) { 65 DefineComponentMetadata metadata = componentMetadatas.get(element); 66 ComponentDescriptor.Builder builder = 67 ComponentDescriptor.builder() 68 .component(ClassName.get(metadata.component())) 69 .scopes(metadata.scopes().stream().map(ClassName::get).collect(toImmutableSet())); 70 71 72 metadata.parentMetadata() 73 .map(DefineComponentMetadata::component) 74 .map(this::componentDescriptor) 75 .ifPresent(builder::parent); 76 77 return builder.build(); 78 } 79 80 /** Returns the set of aggregated {@link ComponentDescriptor}s. */ getComponentDescriptors(Elements elements)81 public ImmutableSet<ComponentDescriptor> getComponentDescriptors(Elements elements) { 82 ImmutableSet<DefineComponentClassesMetadata> aggregatedMetadatas = 83 DefineComponentClassesMetadata.from(elements); 84 85 ImmutableSet<DefineComponentMetadata> components = 86 aggregatedMetadatas.stream() 87 .filter(DefineComponentClassesMetadata::isComponent) 88 .map(DefineComponentClassesMetadata::element) 89 .map(componentMetadatas::get) 90 .collect(toImmutableSet()); 91 92 ImmutableSet<DefineComponentBuilderMetadata> builders = 93 aggregatedMetadatas.stream() 94 .filter(DefineComponentClassesMetadata::isComponentBuilder) 95 .map(DefineComponentClassesMetadata::element) 96 .map(componentBuilderMetadatas::get) 97 .collect(toImmutableSet()); 98 99 ListMultimap<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMultimap = 100 ArrayListMultimap.create(); 101 builders.forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder)); 102 103 // Check that there are not multiple builders per component 104 for (DefineComponentMetadata componentMetadata : builderMultimap.keySet()) { 105 TypeElement component = componentMetadata.component(); 106 ProcessorErrors.checkState( 107 builderMultimap.get(componentMetadata).size() <= 1, 108 component, 109 "Multiple @%s declarations are not allowed for @%s type, %s. Found: %s", 110 ClassNames.DEFINE_COMPONENT_BUILDER, 111 ClassNames.DEFINE_COMPONENT, 112 component, 113 builderMultimap.get(componentMetadata).stream() 114 .map(DefineComponentBuilderMetadata::builder) 115 .map(TypeElement::toString) 116 .sorted() 117 .collect(toImmutableList())); 118 } 119 120 // Now that we know there is at most 1 builder per component, convert the Multimap to Map. 121 Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap = new LinkedHashMap<>(); 122 builderMultimap.entries().forEach(e -> builderMap.put(e.getKey(), e.getValue())); 123 124 return components.stream() 125 .map(componentMetadata -> toComponentDescriptor(componentMetadata, builderMap)) 126 .collect(toImmutableSet()); 127 } 128 toComponentDescriptor( DefineComponentMetadata componentMetadata, Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap)129 private static ComponentDescriptor toComponentDescriptor( 130 DefineComponentMetadata componentMetadata, 131 Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap) { 132 ComponentDescriptor.Builder builder = 133 ComponentDescriptor.builder() 134 .component(ClassName.get(componentMetadata.component())) 135 .scopes( 136 componentMetadata.scopes().stream().map(ClassName::get).collect(toImmutableSet())); 137 138 139 if (builderMap.containsKey(componentMetadata)) { 140 builder.creator(ClassName.get(builderMap.get(componentMetadata).builder())); 141 } 142 143 componentMetadata 144 .parentMetadata() 145 .map(parent -> toComponentDescriptor(parent, builderMap)) 146 .ifPresent(builder::parent); 147 148 return builder.build(); 149 } 150 } 151