1 /* 2 * Copyright (C) 2021 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.spi.model; 18 19 import static com.google.common.base.Preconditions.checkState; 20 import static com.google.common.collect.Iterables.getLast; 21 import static java.util.stream.Collectors.joining; 22 23 import com.google.auto.value.AutoValue; 24 import com.google.auto.value.extension.memoized.Memoized; 25 import com.google.common.collect.ImmutableList; 26 27 /** A path containing a component and all of its ancestor components. */ 28 @AutoValue 29 public abstract class ComponentPath { 30 /** Returns a new {@link ComponentPath} from {@code components}. */ create(Iterable<DaggerTypeElement> components)31 public static ComponentPath create(Iterable<DaggerTypeElement> components) { 32 return new AutoValue_ComponentPath(ImmutableList.copyOf(components)); 33 } 34 35 /** 36 * Returns the component types, starting from the {@linkplain #rootComponent() root 37 * component} and ending with the {@linkplain #currentComponent() current component}. 38 */ components()39 public abstract ImmutableList<DaggerTypeElement> components(); 40 41 /** 42 * Returns the root {@code Component}- or {@code ProductionComponent}-annotated type 43 */ rootComponent()44 public final DaggerTypeElement rootComponent() { 45 return components().get(0); 46 } 47 48 /** Returns the component at the end of the path. */ 49 @Memoized currentComponent()50 public DaggerTypeElement currentComponent() { 51 return getLast(components()); 52 } 53 54 /** 55 * Returns the parent of the {@linkplain #currentComponent()} current component}. 56 * 57 * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component} 58 */ parentComponent()59 public final DaggerTypeElement parentComponent() { 60 checkState(!atRoot()); 61 return components().reverse().get(1); 62 } 63 64 /** 65 * Returns this path's parent path. 66 * 67 * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component} 68 */ 69 // TODO(ronshapiro): consider memoizing this parent()70 public final ComponentPath parent() { 71 checkState(!atRoot()); 72 return create(components().subList(0, components().size() - 1)); 73 } 74 75 /** Returns the path from the root component to the {@code child} of the current component. */ childPath(DaggerTypeElement child)76 public final ComponentPath childPath(DaggerTypeElement child) { 77 return create( 78 ImmutableList.<DaggerTypeElement>builder().addAll(components()).add(child).build()); 79 } 80 81 /** 82 * Returns {@code true} if the {@linkplain #currentComponent()} current component} is the 83 * {@linkplain #rootComponent()} root component}. 84 */ atRoot()85 public final boolean atRoot() { 86 return components().size() == 1; 87 } 88 89 @Override toString()90 public final String toString() { 91 return components().stream().map(Key::qualifiedName).collect(joining(" → ")); 92 } 93 94 @Memoized 95 @Override hashCode()96 public abstract int hashCode(); 97 98 @Override equals(Object obj)99 public abstract boolean equals(Object obj); 100 } 101