• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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