1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2014 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.model; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.common.base.Preconditions.checkNotNull; 20*f585d8a3SJacky Wang import static java.util.stream.Collectors.joining; 21*f585d8a3SJacky Wang 22*f585d8a3SJacky Wang import com.google.auto.common.AnnotationMirrors; 23*f585d8a3SJacky Wang import com.google.auto.common.MoreTypes; 24*f585d8a3SJacky Wang import com.google.auto.value.AutoValue; 25*f585d8a3SJacky Wang import com.google.auto.value.extension.memoized.Memoized; 26*f585d8a3SJacky Wang import com.google.common.base.Equivalence; 27*f585d8a3SJacky Wang import com.google.common.base.Equivalence.Wrapper; 28*f585d8a3SJacky Wang import com.google.common.base.Joiner; 29*f585d8a3SJacky Wang import com.google.common.collect.ImmutableMap; 30*f585d8a3SJacky Wang import com.google.errorprone.annotations.CanIgnoreReturnValue; 31*f585d8a3SJacky Wang import com.squareup.javapoet.CodeBlock; 32*f585d8a3SJacky Wang import java.util.List; 33*f585d8a3SJacky Wang import java.util.Objects; 34*f585d8a3SJacky Wang import java.util.Optional; 35*f585d8a3SJacky Wang import javax.lang.model.element.AnnotationMirror; 36*f585d8a3SJacky Wang import javax.lang.model.element.AnnotationValue; 37*f585d8a3SJacky Wang import javax.lang.model.element.ExecutableElement; 38*f585d8a3SJacky Wang import javax.lang.model.element.TypeElement; 39*f585d8a3SJacky Wang import javax.lang.model.type.TypeMirror; 40*f585d8a3SJacky Wang import javax.lang.model.util.SimpleAnnotationValueVisitor8; 41*f585d8a3SJacky Wang 42*f585d8a3SJacky Wang /** 43*f585d8a3SJacky Wang * A {@linkplain TypeMirror type} and an optional {@linkplain javax.inject.Qualifier qualifier} that 44*f585d8a3SJacky Wang * is the lookup key for a binding. 45*f585d8a3SJacky Wang */ 46*f585d8a3SJacky Wang @AutoValue 47*f585d8a3SJacky Wang public abstract class Key { 48*f585d8a3SJacky Wang /** 49*f585d8a3SJacky Wang * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix 50*f585d8a3SJacky Wang * for the type of this key. 51*f585d8a3SJacky Wang */ qualifier()52*f585d8a3SJacky Wang public final Optional<AnnotationMirror> qualifier() { 53*f585d8a3SJacky Wang return wrappedQualifier().map(Wrapper::get); 54*f585d8a3SJacky Wang } 55*f585d8a3SJacky Wang 56*f585d8a3SJacky Wang /** 57*f585d8a3SJacky Wang * The type represented by this key. 58*f585d8a3SJacky Wang */ type()59*f585d8a3SJacky Wang public final TypeMirror type() { 60*f585d8a3SJacky Wang return wrappedType().get(); 61*f585d8a3SJacky Wang } 62*f585d8a3SJacky Wang 63*f585d8a3SJacky Wang /** 64*f585d8a3SJacky Wang * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix 65*f585d8a3SJacky Wang * for the type of this key. 66*f585d8a3SJacky Wang * 67*f585d8a3SJacky Wang * Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented 68*f585d8a3SJacky Wang * to represent logical equality, so {@link AnnotationMirrors#equivalence()} 69*f585d8a3SJacky Wang * provides this facility. 70*f585d8a3SJacky Wang */ wrappedQualifier()71*f585d8a3SJacky Wang abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier(); 72*f585d8a3SJacky Wang 73*f585d8a3SJacky Wang /** 74*f585d8a3SJacky Wang * The type represented by this key. 75*f585d8a3SJacky Wang * 76*f585d8a3SJacky Wang * As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent 77*f585d8a3SJacky Wang * logical equality, so {@link MoreTypes#equivalence()} wraps this type. 78*f585d8a3SJacky Wang */ wrappedType()79*f585d8a3SJacky Wang abstract Equivalence.Wrapper<TypeMirror> wrappedType(); 80*f585d8a3SJacky Wang 81*f585d8a3SJacky Wang /** 82*f585d8a3SJacky Wang * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link 83*f585d8a3SJacky Wang * #qualifier()}. 84*f585d8a3SJacky Wang * 85*f585d8a3SJacky Wang * <p>Each multibound map and set has a synthetic multibinding that depends on the specific 86*f585d8a3SJacky Wang * contributions to that map or set using keys that identify those multibinding contributions. 87*f585d8a3SJacky Wang * 88*f585d8a3SJacky Wang * <p>Absent except for multibinding contributions. 89*f585d8a3SJacky Wang */ multibindingContributionIdentifier()90*f585d8a3SJacky Wang public abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier(); 91*f585d8a3SJacky Wang 92*f585d8a3SJacky Wang /** Returns a {@link Builder} that inherits the properties of this key. */ toBuilder()93*f585d8a3SJacky Wang public abstract Builder toBuilder(); 94*f585d8a3SJacky Wang 95*f585d8a3SJacky Wang // The main hashCode/equality bottleneck is in MoreTypes.equivalence(). It's possible that we can 96*f585d8a3SJacky Wang // avoid this by tuning that method. Perhaps we can also avoid the issue entirely by interning all 97*f585d8a3SJacky Wang // Keys 98*f585d8a3SJacky Wang @Memoized 99*f585d8a3SJacky Wang @Override hashCode()100*f585d8a3SJacky Wang public abstract int hashCode(); 101*f585d8a3SJacky Wang 102*f585d8a3SJacky Wang @Override equals(Object o)103*f585d8a3SJacky Wang public abstract boolean equals(Object o); 104*f585d8a3SJacky Wang 105*f585d8a3SJacky Wang /** 106*f585d8a3SJacky Wang * Returns a String rendering of an {@link AnnotationMirror} that includes attributes in the order 107*f585d8a3SJacky Wang * defined in the annotation type. This will produce the same output for {@linkplain 108*f585d8a3SJacky Wang * AnnotationMirrors#equivalence() equal} {@link AnnotationMirror}s even if default values are 109*f585d8a3SJacky Wang * omitted or their attributes were written in different orders, e.g. {@code @A(b = "b", c = "c")} 110*f585d8a3SJacky Wang * and {@code @A(c = "c", b = "b", attributeWithDefaultValue = "default value")}. 111*f585d8a3SJacky Wang */ 112*f585d8a3SJacky Wang // TODO(ronshapiro): move this to auto-common stableAnnotationMirrorToString(AnnotationMirror qualifier)113*f585d8a3SJacky Wang static String stableAnnotationMirrorToString(AnnotationMirror qualifier) { 114*f585d8a3SJacky Wang StringBuilder builder = new StringBuilder("@").append(qualifier.getAnnotationType()); 115*f585d8a3SJacky Wang ImmutableMap<ExecutableElement, AnnotationValue> elementValues = 116*f585d8a3SJacky Wang AnnotationMirrors.getAnnotationValuesWithDefaults(qualifier); 117*f585d8a3SJacky Wang if (!elementValues.isEmpty()) { 118*f585d8a3SJacky Wang ImmutableMap.Builder<String, String> namedValuesBuilder = ImmutableMap.builder(); 119*f585d8a3SJacky Wang elementValues.forEach( 120*f585d8a3SJacky Wang (key, value) -> 121*f585d8a3SJacky Wang namedValuesBuilder.put( 122*f585d8a3SJacky Wang key.getSimpleName().toString(), stableAnnotationValueToString(value))); 123*f585d8a3SJacky Wang ImmutableMap<String, String> namedValues = namedValuesBuilder.build(); 124*f585d8a3SJacky Wang builder.append('('); 125*f585d8a3SJacky Wang if (namedValues.size() == 1 && namedValues.containsKey("value")) { 126*f585d8a3SJacky Wang // Omit "value =" 127*f585d8a3SJacky Wang builder.append(namedValues.get("value")); 128*f585d8a3SJacky Wang } else { 129*f585d8a3SJacky Wang builder.append(Joiner.on(", ").withKeyValueSeparator("=").join(namedValues)); 130*f585d8a3SJacky Wang } 131*f585d8a3SJacky Wang builder.append(')'); 132*f585d8a3SJacky Wang } 133*f585d8a3SJacky Wang return builder.toString(); 134*f585d8a3SJacky Wang } 135*f585d8a3SJacky Wang stableAnnotationValueToString(AnnotationValue annotationValue)136*f585d8a3SJacky Wang private static String stableAnnotationValueToString(AnnotationValue annotationValue) { 137*f585d8a3SJacky Wang return annotationValue.accept( 138*f585d8a3SJacky Wang new SimpleAnnotationValueVisitor8<String, Void>() { 139*f585d8a3SJacky Wang @Override 140*f585d8a3SJacky Wang protected String defaultAction(Object value, Void ignore) { 141*f585d8a3SJacky Wang return value.toString(); 142*f585d8a3SJacky Wang } 143*f585d8a3SJacky Wang 144*f585d8a3SJacky Wang @Override 145*f585d8a3SJacky Wang public String visitString(String value, Void ignore) { 146*f585d8a3SJacky Wang return CodeBlock.of("$S", value).toString(); 147*f585d8a3SJacky Wang } 148*f585d8a3SJacky Wang 149*f585d8a3SJacky Wang @Override 150*f585d8a3SJacky Wang public String visitAnnotation(AnnotationMirror value, Void ignore) { 151*f585d8a3SJacky Wang return stableAnnotationMirrorToString(value); 152*f585d8a3SJacky Wang } 153*f585d8a3SJacky Wang 154*f585d8a3SJacky Wang @Override 155*f585d8a3SJacky Wang public String visitArray(List<? extends AnnotationValue> value, Void ignore) { 156*f585d8a3SJacky Wang return value.stream() 157*f585d8a3SJacky Wang .map(Key::stableAnnotationValueToString) 158*f585d8a3SJacky Wang .collect(joining(", ", "{", "}")); 159*f585d8a3SJacky Wang } 160*f585d8a3SJacky Wang }, 161*f585d8a3SJacky Wang null); 162*f585d8a3SJacky Wang } 163*f585d8a3SJacky Wang 164*f585d8a3SJacky Wang @Override 165*f585d8a3SJacky Wang public final String toString() { 166*f585d8a3SJacky Wang return Joiner.on(' ') 167*f585d8a3SJacky Wang .skipNulls() 168*f585d8a3SJacky Wang .join( 169*f585d8a3SJacky Wang qualifier().map(Key::stableAnnotationMirrorToString).orElse(null), 170*f585d8a3SJacky Wang type(), 171*f585d8a3SJacky Wang multibindingContributionIdentifier().orElse(null)); 172*f585d8a3SJacky Wang } 173*f585d8a3SJacky Wang 174*f585d8a3SJacky Wang /** Returns a builder for {@link Key}s. */ 175*f585d8a3SJacky Wang public static Builder builder(TypeMirror type) { 176*f585d8a3SJacky Wang return new AutoValue_Key.Builder().type(type); 177*f585d8a3SJacky Wang } 178*f585d8a3SJacky Wang 179*f585d8a3SJacky Wang /** A builder for {@link Key}s. */ 180*f585d8a3SJacky Wang @AutoValue.Builder 181*f585d8a3SJacky Wang public abstract static class Builder { 182*f585d8a3SJacky Wang abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType); 183*f585d8a3SJacky Wang 184*f585d8a3SJacky Wang @CanIgnoreReturnValue 185*f585d8a3SJacky Wang public final Builder type(TypeMirror type) { 186*f585d8a3SJacky Wang return wrappedType(MoreTypes.equivalence().wrap(checkNotNull(type))); 187*f585d8a3SJacky Wang } 188*f585d8a3SJacky Wang 189*f585d8a3SJacky Wang abstract Builder wrappedQualifier( 190*f585d8a3SJacky Wang Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier); 191*f585d8a3SJacky Wang 192*f585d8a3SJacky Wang abstract Builder wrappedQualifier(Equivalence.Wrapper<AnnotationMirror> wrappedQualifier); 193*f585d8a3SJacky Wang 194*f585d8a3SJacky Wang @CanIgnoreReturnValue 195*f585d8a3SJacky Wang public final Builder qualifier(AnnotationMirror qualifier) { 196*f585d8a3SJacky Wang return wrappedQualifier(AnnotationMirrors.equivalence().wrap(checkNotNull(qualifier))); 197*f585d8a3SJacky Wang } 198*f585d8a3SJacky Wang 199*f585d8a3SJacky Wang @CanIgnoreReturnValue 200*f585d8a3SJacky Wang public final Builder qualifier(Optional<AnnotationMirror> qualifier) { 201*f585d8a3SJacky Wang return wrappedQualifier(checkNotNull(qualifier).map(AnnotationMirrors.equivalence()::wrap)); 202*f585d8a3SJacky Wang } 203*f585d8a3SJacky Wang 204*f585d8a3SJacky Wang public abstract Builder multibindingContributionIdentifier( 205*f585d8a3SJacky Wang Optional<MultibindingContributionIdentifier> identifier); 206*f585d8a3SJacky Wang 207*f585d8a3SJacky Wang public abstract Builder multibindingContributionIdentifier( 208*f585d8a3SJacky Wang MultibindingContributionIdentifier identifier); 209*f585d8a3SJacky Wang 210*f585d8a3SJacky Wang public abstract Key build(); 211*f585d8a3SJacky Wang } 212*f585d8a3SJacky Wang 213*f585d8a3SJacky Wang /** 214*f585d8a3SJacky Wang * An object that identifies a multibinding contribution method and the module class that 215*f585d8a3SJacky Wang * contributes it to the graph. 216*f585d8a3SJacky Wang * 217*f585d8a3SJacky Wang * @see #multibindingContributionIdentifier() 218*f585d8a3SJacky Wang */ 219*f585d8a3SJacky Wang public static final class MultibindingContributionIdentifier { 220*f585d8a3SJacky Wang private final String module; 221*f585d8a3SJacky Wang private final String bindingElement; 222*f585d8a3SJacky Wang 223*f585d8a3SJacky Wang /** 224*f585d8a3SJacky Wang * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 225*f585d8a3SJacky Wang * It is not part of a specified API and may change at any point. 226*f585d8a3SJacky Wang */ 227*f585d8a3SJacky Wang @Deprecated 228*f585d8a3SJacky Wang public MultibindingContributionIdentifier( 229*f585d8a3SJacky Wang // TODO(ronshapiro): reverse the order of these parameters 230*f585d8a3SJacky Wang ExecutableElement bindingMethod, TypeElement contributingModule) { 231*f585d8a3SJacky Wang this( 232*f585d8a3SJacky Wang bindingMethod.getSimpleName().toString(), 233*f585d8a3SJacky Wang contributingModule.getQualifiedName().toString()); 234*f585d8a3SJacky Wang } 235*f585d8a3SJacky Wang 236*f585d8a3SJacky Wang // TODO(ronshapiro,dpb): create KeyProxies so that these constructors don't need to be public. 237*f585d8a3SJacky Wang @Deprecated 238*f585d8a3SJacky Wang public MultibindingContributionIdentifier(String bindingElement, String module) { 239*f585d8a3SJacky Wang this.module = module; 240*f585d8a3SJacky Wang this.bindingElement = bindingElement; 241*f585d8a3SJacky Wang } 242*f585d8a3SJacky Wang 243*f585d8a3SJacky Wang /** 244*f585d8a3SJacky Wang * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 245*f585d8a3SJacky Wang * It is not part of a specified API and may change at any point. 246*f585d8a3SJacky Wang */ 247*f585d8a3SJacky Wang @Deprecated 248*f585d8a3SJacky Wang public String module() { 249*f585d8a3SJacky Wang return module; 250*f585d8a3SJacky Wang } 251*f585d8a3SJacky Wang 252*f585d8a3SJacky Wang /** 253*f585d8a3SJacky Wang * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}. 254*f585d8a3SJacky Wang * It is not part of a specified API and may change at any point. 255*f585d8a3SJacky Wang */ 256*f585d8a3SJacky Wang @Deprecated 257*f585d8a3SJacky Wang public String bindingElement() { 258*f585d8a3SJacky Wang return bindingElement; 259*f585d8a3SJacky Wang } 260*f585d8a3SJacky Wang 261*f585d8a3SJacky Wang /** 262*f585d8a3SJacky Wang * {@inheritDoc} 263*f585d8a3SJacky Wang * 264*f585d8a3SJacky Wang * <p>The returned string is human-readable and distinguishes the keys in the same way as the 265*f585d8a3SJacky Wang * whole object. 266*f585d8a3SJacky Wang */ 267*f585d8a3SJacky Wang @Override 268*f585d8a3SJacky Wang public String toString() { 269*f585d8a3SJacky Wang return String.format("%s#%s", module, bindingElement); 270*f585d8a3SJacky Wang } 271*f585d8a3SJacky Wang 272*f585d8a3SJacky Wang @Override 273*f585d8a3SJacky Wang public boolean equals(Object obj) { 274*f585d8a3SJacky Wang if (obj instanceof MultibindingContributionIdentifier) { 275*f585d8a3SJacky Wang MultibindingContributionIdentifier other = (MultibindingContributionIdentifier) obj; 276*f585d8a3SJacky Wang return module.equals(other.module) && bindingElement.equals(other.bindingElement); 277*f585d8a3SJacky Wang } 278*f585d8a3SJacky Wang return false; 279*f585d8a3SJacky Wang } 280*f585d8a3SJacky Wang 281*f585d8a3SJacky Wang @Override 282*f585d8a3SJacky Wang public int hashCode() { 283*f585d8a3SJacky Wang return Objects.hash(module, bindingElement); 284*f585d8a3SJacky Wang } 285*f585d8a3SJacky Wang } 286*f585d8a3SJacky Wang } 287