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