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