• 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.internal.codegen.xprocessing;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
20 import static androidx.room.compiler.processing.compat.XConverters.toJavac;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
22 import static java.util.stream.Collectors.joining;
23 
24 import androidx.room.compiler.processing.JavaPoetExtKt;
25 import androidx.room.compiler.processing.XAnnotation;
26 import androidx.room.compiler.processing.XProcessingEnv;
27 import androidx.room.compiler.processing.XType;
28 import androidx.room.compiler.processing.XTypeElement;
29 import com.google.auto.common.AnnotationMirrors;
30 import com.google.common.base.Equivalence;
31 import com.google.common.collect.ImmutableList;
32 import com.squareup.javapoet.AnnotationSpec;
33 import com.squareup.javapoet.ClassName;
34 import java.util.Arrays;
35 
36 // TODO(bcorso): Consider moving these methods into XProcessing library.
37 /** A utility class for {@link XAnnotation} helper methods. */
38 public final class XAnnotations {
39 
40   /** Returns the {@link AnnotationSpec} for the given annotation */
getAnnotationSpec(XAnnotation annotation)41   public static AnnotationSpec getAnnotationSpec(XAnnotation annotation) {
42     return JavaPoetExtKt.toAnnotationSpec(annotation, false);
43   }
44 
45   /** Returns the string representation of the given annotation. */
toString(XAnnotation annotation)46   public static String toString(XAnnotation annotation) {
47     // TODO(b/241293838): Make javac and ksp agree on the string representation.
48     return getProcessingEnv(annotation).getBackend() == XProcessingEnv.Backend.JAVAC
49         ? AnnotationMirrors.toString(toJavac(annotation))
50         : XAnnotations.toStableString(annotation);
51   }
52 
53   /** Returns the class name of the given annotation */
getClassName(XAnnotation annotation)54   public static ClassName getClassName(XAnnotation annotation) {
55     return annotation.getType().getTypeElement().getClassName();
56   }
57 
58   private static final Equivalence<XAnnotation> XANNOTATION_EQUIVALENCE =
59       new Equivalence<XAnnotation>() {
60         @Override
61         protected boolean doEquivalent(XAnnotation left, XAnnotation right) {
62           return XTypes.equivalence().equivalent(left.getType(), right.getType())
63               && XAnnotationValues.equivalence()
64                   .pairwise()
65                   .equivalent(left.getAnnotationValues(), right.getAnnotationValues());
66         }
67 
68         @Override
69         protected int doHash(XAnnotation annotation) {
70           return Arrays.hashCode(
71               new int[] {
72                 XTypes.equivalence().hash(annotation.getType()),
73                 XAnnotationValues.equivalence().pairwise().hash(annotation.getAnnotationValues())
74               });
75         }
76 
77         @Override
78         public String toString() {
79           return "XAnnotation.equivalence()";
80         }
81       };
82 
83   /**
84    * Returns an {@link Equivalence} for {@link XAnnotation}.
85    *
86    * <p>This equivalence takes into account the order of annotation values.
87    */
equivalence()88   public static Equivalence<XAnnotation> equivalence() {
89     return XANNOTATION_EQUIVALENCE;
90   }
91 
92   /**
93    * Returns a stable string representation of {@link XAnnotation}.
94    *
95    * <p>The output string will be the same regardless of whether default values were omitted or
96    * their attributes were written in different orders, e.g. {@code @A(b = "b", c = "c")} and
97    * {@code @A(c = "c", b = "b", attributeWithDefaultValue = "default value")} will both output the
98    * same string. This stability can be useful for things like serialization or reporting error
99    * messages.
100    */
toStableString(XAnnotation annotation)101   public static String toStableString(XAnnotation annotation) {
102     try {
103       // TODO(b/249283155): Due to a bug in XProcessing, calling various methods on an annotation
104       // that is an error type may throw an unexpected exception, so we just output the name.
105       if (annotation.getType().isError()) {
106         return "@" + annotation.getName(); // SUPPRESS_GET_NAME_CHECK
107       }
108       return annotation.getAnnotationValues().isEmpty()
109           // If the annotation doesn't have values then skip the empty parenthesis.
110           ? String.format("@%s", getClassName(annotation).canonicalName())
111           : String.format(
112               "@%s(%s)",
113               getClassName(annotation).canonicalName(),
114               // The annotation values returned by XProcessing should already be in the order
115               // defined in the annotation class and include default values for any missing values.
116               annotation.getAnnotationValues().stream()
117                   .map(
118                       value -> {
119                         String name = value.getName(); // SUPPRESS_GET_NAME_CHECK
120                         String valueAsString = XAnnotationValues.toStableString(value);
121                         // A single value with name "value" can output the value directly.
122                         return annotation.getAnnotationValues().size() == 1
123                                 && name.contentEquals("value")
124                             ? valueAsString
125                             : String.format("%s=%s", name, valueAsString);
126                       })
127                   .collect(joining(", ")));
128     } catch (TypeNotPresentException e) {
129       return e.typeName();
130     }
131   }
132 
133   /** Returns the value of the given [key] as a type element. */
getAsTypeElement(XAnnotation annotation, String key)134   public static XTypeElement getAsTypeElement(XAnnotation annotation, String key) {
135     return annotation.getAsType(key).getTypeElement();
136   }
137 
138   /** Returns the value of the given [key] as a list of type elements. */
getAsTypeElementList( XAnnotation annotation, String key)139   public static ImmutableList<XTypeElement> getAsTypeElementList(
140       XAnnotation annotation, String key) {
141     return annotation.getAsTypeList(key).stream()
142         .map(XType::getTypeElement)
143         .collect(toImmutableList());
144   }
145 
XAnnotations()146   private XAnnotations() {}
147 }
148