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