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; 18 19 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 20 21 import com.google.common.base.Preconditions; 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.Iterables; 25 import com.google.common.graph.GraphBuilder; 26 import com.google.common.graph.Graphs; 27 import com.google.common.graph.ImmutableGraph; 28 import com.google.common.graph.MutableGraph; 29 import com.squareup.javapoet.ClassName; 30 import java.util.HashMap; 31 import java.util.Map; 32 import java.util.Set; 33 34 /** A representation of the full tree of scopes. */ 35 public final class ComponentTree { 36 private final ImmutableGraph<ComponentDescriptor> graph; 37 private final ComponentDescriptor root; 38 39 /** Creates a new tree from a set of descriptors. */ from(Set<ComponentDescriptor> descriptors)40 public static ComponentTree from(Set<ComponentDescriptor> descriptors) { 41 MutableGraph<ComponentDescriptor> graph = 42 GraphBuilder.directed().allowsSelfLoops(false).build(); 43 44 descriptors.forEach( 45 descriptor -> { 46 graph.addNode(descriptor); 47 descriptor.parent().ifPresent(parent -> graph.putEdge(parent, descriptor)); 48 }); 49 50 return new ComponentTree(ImmutableGraph.copyOf(graph)); 51 } 52 ComponentTree(ImmutableGraph<ComponentDescriptor> graph)53 private ComponentTree(ImmutableGraph<ComponentDescriptor> graph) { 54 this.graph = Preconditions.checkNotNull(graph); 55 Preconditions.checkState( 56 !Graphs.hasCycle(graph), 57 "Component graph has cycles: %s", 58 graph.nodes()); 59 60 // Check that each component has a unique descriptor 61 Map<ClassName, ComponentDescriptor> descriptors = new HashMap<>(); 62 for (ComponentDescriptor descriptor : graph.nodes()) { 63 if (descriptors.containsKey(descriptor.component())) { 64 ComponentDescriptor prevDescriptor = descriptors.get(descriptor.component()); 65 Preconditions.checkState( 66 // TODO(b/144939893): Use "==" instead of ".equals()"? 67 descriptor.equals(prevDescriptor), 68 "%s has mismatching descriptors:\n" 69 + " %s\n\n" 70 + " %s\n\n", 71 prevDescriptor.component(), 72 prevDescriptor, 73 descriptor); 74 } 75 descriptors.put(descriptor.component(), descriptor); 76 } 77 78 ImmutableList<ComponentDescriptor> roots = 79 graph.nodes().stream() 80 .filter(node -> graph.inDegree(node) == 0) 81 .collect(toImmutableList()); 82 83 Preconditions.checkState( 84 roots.size() == 1, 85 "Component graph must have exactly 1 root. Found: %s", 86 roots.stream().map(ComponentDescriptor::component).collect(toImmutableList())); 87 88 root = Iterables.getOnlyElement(roots); 89 } 90 getComponentDescriptors()91 public ImmutableSet<ComponentDescriptor> getComponentDescriptors() { 92 return ImmutableSet.copyOf(graph.nodes()); 93 } 94 childrenOf(ComponentDescriptor componentDescriptor)95 public ImmutableSet<ComponentDescriptor> childrenOf(ComponentDescriptor componentDescriptor) { 96 return ImmutableSet.copyOf(graph.successors(componentDescriptor)); 97 } 98 graph()99 public ImmutableGraph<ComponentDescriptor> graph() { 100 return graph; 101 } 102 root()103 public ComponentDescriptor root() { 104 return root; 105 } 106 } 107