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 20 import com.google.auto.value.AutoValue; 21 import com.google.auto.value.extension.memoized.Memoized; 22 import com.google.common.base.Joiner; 23 import java.util.Optional; 24 25 /** 26 * A {@linkplain DaggerType type} and an optional {@linkplain javax.inject.Qualifier qualifier} that 27 * is the lookup key for a binding. 28 */ 29 @AutoValue 30 public abstract class Key { 31 /** 32 * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix for the 33 * type of this key. 34 */ qualifier()35 public abstract Optional<DaggerAnnotation> qualifier(); 36 37 /** The type represented by this key. */ type()38 public abstract DaggerType type(); 39 40 /** 41 * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link 42 * #qualifier()}. 43 * 44 * <p>Each multibound map and set has a synthetic multibinding that depends on the specific 45 * contributions to that map or set using keys that identify those multibinding contributions. 46 * 47 * <p>Absent except for multibinding contributions. 48 */ multibindingContributionIdentifier()49 public abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier(); 50 51 /** Returns a {@link Builder} that inherits the properties of this key. */ toBuilder()52 abstract Builder toBuilder(); 53 54 /** Returns a copy of this key with the type replaced with the given type. */ withType(DaggerType newType)55 public Key withType(DaggerType newType) { 56 return toBuilder().type(newType).build(); 57 } 58 59 /** 60 * Returns a copy of this key with the multibinding contribution identifier replaced with the 61 * given multibinding contribution identifier. 62 */ withMultibindingContributionIdentifier( DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod)63 public Key withMultibindingContributionIdentifier( 64 DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod) { 65 return toBuilder() 66 .multibindingContributionIdentifier(contributingModule, bindingMethod) 67 .build(); 68 } 69 70 /** Returns a copy of this key with the multibinding contribution identifier, if any, removed. */ withoutMultibindingContributionIdentifier()71 public Key withoutMultibindingContributionIdentifier() { 72 return toBuilder().multibindingContributionIdentifier(Optional.empty()).build(); 73 } 74 75 // The main hashCode/equality bottleneck is in MoreTypes.equivalence(). It's possible that we can 76 // avoid this by tuning that method. Perhaps we can also avoid the issue entirely by interning all 77 // Keys 78 @Memoized 79 @Override hashCode()80 public abstract int hashCode(); 81 82 @Override equals(Object o)83 public abstract boolean equals(Object o); 84 85 @Override toString()86 public final String toString() { 87 return Joiner.on(' ') 88 .skipNulls() 89 .join( 90 qualifier().map(DaggerAnnotation::toString).orElse(null), 91 type(), 92 multibindingContributionIdentifier().orElse(null)); 93 } 94 95 /** Returns a builder for {@link Key}s. */ builder(DaggerType type)96 public static Builder builder(DaggerType type) { 97 return new AutoValue_Key.Builder().type(type); 98 } 99 100 /** A builder for {@link Key}s. */ 101 @AutoValue.Builder 102 public abstract static class Builder { type(DaggerType type)103 public abstract Builder type(DaggerType type); 104 qualifier(Optional<DaggerAnnotation> qualifier)105 public abstract Builder qualifier(Optional<DaggerAnnotation> qualifier); 106 qualifier(DaggerAnnotation qualifier)107 public abstract Builder qualifier(DaggerAnnotation qualifier); 108 multibindingContributionIdentifier( DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod)109 public final Builder multibindingContributionIdentifier( 110 DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod) { 111 return multibindingContributionIdentifier( 112 Optional.of( 113 MultibindingContributionIdentifier.create(contributingModule, bindingMethod))); 114 } 115 multibindingContributionIdentifier( Optional<MultibindingContributionIdentifier> identifier)116 abstract Builder multibindingContributionIdentifier( 117 Optional<MultibindingContributionIdentifier> identifier); 118 build()119 public abstract Key build(); 120 } 121 122 /** 123 * An object that identifies a multibinding contribution method and the module class that 124 * contributes it to the graph. 125 * 126 * @see #multibindingContributionIdentifier() 127 */ 128 @AutoValue 129 public abstract static class MultibindingContributionIdentifier { create( DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod)130 private static MultibindingContributionIdentifier create( 131 DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod) { 132 return new AutoValue_Key_MultibindingContributionIdentifier( 133 qualifiedName(contributingModule), simpleName(bindingMethod)); 134 } 135 136 /** Returns the module containing the multibinding method. */ contributingModule()137 public abstract String contributingModule(); 138 139 /** Returns the multibinding method that defines teh multibinding contribution. */ bindingMethod()140 public abstract String bindingMethod(); 141 142 /** 143 * {@inheritDoc} 144 * 145 * <p>The returned string is human-readable and distinguishes the keys in the same way as the 146 * whole object. 147 */ 148 @Override toString()149 public final String toString() { 150 return String.format("%s#%s", contributingModule(), bindingMethod()); 151 } 152 } 153 qualifiedName(DaggerTypeElement element)154 static String qualifiedName(DaggerTypeElement element) { 155 switch (element.backend()) { 156 case JAVAC: 157 return element.javac().getQualifiedName().toString(); 158 case KSP: 159 return element.ksp().getQualifiedName().asString(); 160 } 161 throw new IllegalStateException("Unknown backend: " + element.backend()); 162 } 163 simpleName(DaggerExecutableElement element)164 private static String simpleName(DaggerExecutableElement element) { 165 switch (element.backend()) { 166 case JAVAC: 167 return element.javac().getSimpleName().toString(); 168 case KSP: 169 return element.ksp().getSimpleName().asString(); 170 } 171 throw new IllegalStateException("Unknown backend: " + element.backend()); 172 } 173 } 174