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 com.google.auto.value.AutoValue; 20 import com.google.auto.value.extension.memoized.Memoized; 21 import com.google.common.base.Joiner; 22 import com.google.errorprone.annotations.CanIgnoreReturnValue; 23 import com.google.errorprone.annotations.CheckReturnValue; 24 import java.util.Objects; 25 import java.util.Optional; 26 import javax.lang.model.element.ExecutableElement; 27 import javax.lang.model.element.TypeElement; 28 29 /** 30 * A {@linkplain DaggerType type} and an optional {@linkplain javax.inject.Qualifier qualifier} that 31 * is the lookup key for a binding. 32 */ 33 @AutoValue 34 public abstract class Key { 35 /** 36 * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix for the 37 * type of this key. 38 */ qualifier()39 public abstract Optional<DaggerAnnotation> qualifier(); 40 41 /** The type represented by this key. */ type()42 public abstract DaggerType type(); 43 44 /** 45 * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link 46 * #qualifier()}. 47 * 48 * <p>Each multibound map and set has a synthetic multibinding that depends on the specific 49 * contributions to that map or set using keys that identify those multibinding contributions. 50 * 51 * <p>Absent except for multibinding contributions. 52 */ multibindingContributionIdentifier()53 public abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier(); 54 55 /** Returns a {@link Builder} that inherits the properties of this key. */ toBuilder()56 public abstract Builder toBuilder(); 57 58 // The main hashCode/equality bottleneck is in MoreTypes.equivalence(). It's possible that we can 59 // avoid this by tuning that method. Perhaps we can also avoid the issue entirely by interning all 60 // Keys 61 @Memoized 62 @Override hashCode()63 public abstract int hashCode(); 64 65 @Override equals(Object o)66 public abstract boolean equals(Object o); 67 68 @Override toString()69 public final String toString() { 70 return Joiner.on(' ') 71 .skipNulls() 72 .join( 73 qualifier().map(MoreAnnotationMirrors::toStableString).orElse(null), 74 type(), 75 multibindingContributionIdentifier().orElse(null)); 76 } 77 78 /** Returns a builder for {@link Key}s. */ builder(DaggerType type)79 public static Builder builder(DaggerType type) { 80 return new AutoValue_Key.Builder().type(type); 81 } 82 83 /** A builder for {@link Key}s. */ 84 @CanIgnoreReturnValue 85 @AutoValue.Builder 86 public abstract static class Builder { type(DaggerType type)87 public abstract Builder type(DaggerType type); 88 qualifier(Optional<DaggerAnnotation> qualifier)89 public abstract Builder qualifier(Optional<DaggerAnnotation> qualifier); 90 qualifier(DaggerAnnotation qualifier)91 public abstract Builder qualifier(DaggerAnnotation qualifier); 92 multibindingContributionIdentifier( Optional<MultibindingContributionIdentifier> identifier)93 public abstract Builder multibindingContributionIdentifier( 94 Optional<MultibindingContributionIdentifier> identifier); 95 multibindingContributionIdentifier( MultibindingContributionIdentifier identifier)96 public abstract Builder multibindingContributionIdentifier( 97 MultibindingContributionIdentifier identifier); 98 99 @CheckReturnValue build()100 public abstract Key build(); 101 } 102 103 /** 104 * An object that identifies a multibinding contribution method and the module class that 105 * contributes it to the graph. 106 * 107 * @see #multibindingContributionIdentifier() 108 */ 109 public static final class MultibindingContributionIdentifier { 110 private final String module; 111 private final String bindingElement; 112 113 /** 114 * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 115 * It is not part of a specified API and may change at any point. 116 */ 117 @Deprecated MultibindingContributionIdentifier( ExecutableElement bindingMethod, TypeElement contributingModule)118 public MultibindingContributionIdentifier( 119 // TODO(ronshapiro): reverse the order of these parameters 120 ExecutableElement bindingMethod, TypeElement contributingModule) { 121 this( 122 bindingMethod.getSimpleName().toString(), 123 contributingModule.getQualifiedName().toString()); 124 } 125 126 // TODO(ronshapiro,dpb): create KeyProxies so that these constructors don't need to be public. 127 @Deprecated MultibindingContributionIdentifier(String bindingElement, String module)128 public MultibindingContributionIdentifier(String bindingElement, String module) { 129 this.module = module; 130 this.bindingElement = bindingElement; 131 } 132 133 /** 134 * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 135 * It is not part of a specified API and may change at any point. 136 */ 137 @Deprecated module()138 public String module() { 139 return module; 140 } 141 142 /** 143 * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 144 * It is not part of a specified API and may change at any point. 145 */ 146 @Deprecated bindingElement()147 public String bindingElement() { 148 return bindingElement; 149 } 150 151 /** 152 * {@inheritDoc} 153 * 154 * <p>The returned string is human-readable and distinguishes the keys in the same way as the 155 * whole object. 156 */ 157 @Override toString()158 public String toString() { 159 return String.format("%s#%s", module, bindingElement); 160 } 161 162 @Override equals(Object obj)163 public boolean equals(Object obj) { 164 if (obj instanceof MultibindingContributionIdentifier) { 165 MultibindingContributionIdentifier other = (MultibindingContributionIdentifier) obj; 166 return module.equals(other.module) && bindingElement.equals(other.bindingElement); 167 } 168 return false; 169 } 170 171 @Override hashCode()172 public int hashCode() { 173 return Objects.hash(module, bindingElement); 174 } 175 } 176 } 177