• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.binding;
18 
19 import static androidx.room.compiler.processing.XTypeKt.isArray;
20 import static dagger.internal.codegen.binding.SourceFiles.classFileName;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
22 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
23 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
24 import static dagger.internal.codegen.xprocessing.XAnnotationValues.characterLiteralWithSingleQuotes;
25 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
26 import static dagger.internal.codegen.xprocessing.XTypes.asArray;
27 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
28 
29 import androidx.room.compiler.processing.XAnnotation;
30 import androidx.room.compiler.processing.XAnnotationValue;
31 import androidx.room.compiler.processing.XType;
32 import androidx.room.compiler.processing.XTypeElement;
33 import com.squareup.javapoet.ClassName;
34 import com.squareup.javapoet.CodeBlock;
35 import dagger.internal.codegen.javapoet.TypeNames;
36 
37 /**
38  * Returns an expression creating an instance of the visited annotation type. Its parameter must be
39  * a class as generated by {@link dagger.internal.codegen.writing.AnnotationCreatorGenerator}.
40  *
41  * <p>Note that {@code AnnotationValue#toString()} is the source-code representation of the value
42  * <em>when used in an annotation</em>, which is not always the same as the representation needed
43  * when creating the value in a method body.
44  *
45  * <p>For example, inside an annotation, a nested array of {@code int}s is simply {@code {1, 2, 3}},
46  * but in code it would have to be {@code new int[] {1, 2, 3}}.
47  */
48 public final class AnnotationExpression {
49   private final XAnnotation annotation;
50   private final ClassName creatorClass;
51 
AnnotationExpression(XAnnotation annotation)52   AnnotationExpression(XAnnotation annotation) {
53     this.annotation = annotation;
54     this.creatorClass = getAnnotationCreatorClassName(annotation.getType().getTypeElement());
55   }
56 
57   /**
58    * Returns an expression that calls static methods on the annotation's creator class to create an
59    * annotation instance equivalent the annotation passed to the constructor.
60    */
getAnnotationInstanceExpression()61   CodeBlock getAnnotationInstanceExpression() {
62     return getAnnotationInstanceExpression(annotation);
63   }
64 
getAnnotationInstanceExpression(XAnnotation annotation)65   private CodeBlock getAnnotationInstanceExpression(XAnnotation annotation) {
66     return CodeBlock.of(
67         "$T.$L($L)",
68         creatorClass,
69         createMethodName(annotation.getType().getTypeElement()),
70         makeParametersCodeBlock(
71             annotation.getAnnotationValues().stream()
72                 .map(this::getValueExpression)
73                 .collect(toImmutableList())));
74   }
75 
76   /**
77    * Returns the name of the generated class that contains the static {@code create} methods for an
78    * annotation type.
79    */
getAnnotationCreatorClassName(XTypeElement annotationType)80   public static ClassName getAnnotationCreatorClassName(XTypeElement annotationType) {
81     ClassName annotationTypeName = annotationType.getClassName();
82     return annotationTypeName
83         .topLevelClassName()
84         .peerClass(classFileName(annotationTypeName) + "Creator");
85   }
86 
createMethodName(XTypeElement annotationType)87   public static String createMethodName(XTypeElement annotationType) {
88     return "create" + getSimpleName(annotationType);
89   }
90 
91   /**
92    * Returns an expression that evaluates to a {@code value} of a given type on an {@code
93    * annotation}.
94    */
getValueExpression(XAnnotationValue value)95   CodeBlock getValueExpression(XAnnotationValue value) {
96     if (isArray(value.getValueType())) {
97       XType componentType = asArray(value.getValueType()).getComponentType();
98       return CodeBlock.of(
99           "new $T[] {$L}",
100           // TODO(b/264464791): The KClass -> Class swap can be removed once this bug is fixed.
101           isTypeOf(componentType, TypeNames.KCLASS)
102               ? TypeNames.CLASS
103               : componentType.getRawType().getTypeName(),
104           value.asAnnotationValueList().stream()
105               .map(this::getValueExpression)
106               .collect(toParametersCodeBlock()));
107     } else if (value.hasEnumValue()) {
108       return CodeBlock.of(
109           "$T.$L",
110           value.asEnum().getEnclosingElement().getClassName(),
111           getSimpleName(value.asEnum()));
112     } else if (value.hasAnnotationValue()) {
113       return getAnnotationInstanceExpression(value.asAnnotation());
114     } else if (value.hasTypeValue()) {
115       return CodeBlock.of("$T.class", value.asType().getTypeElement().getClassName());
116     } else if (value.hasStringValue()) {
117       return CodeBlock.of("$S", value.asString());
118     } else if (value.hasByteValue()) {
119       return CodeBlock.of("(byte) $L", value.asByte());
120     } else if (value.hasCharValue()) {
121       // TODO(bcorso): Replace when https://github.com/square/javapoet/issues/698 is fixed.
122       return CodeBlock.of("$L", characterLiteralWithSingleQuotes(value.asChar()));
123     } else if (value.hasDoubleValue()) {
124       return CodeBlock.of("$LD", value.asDouble());
125     } else if (value.hasFloatValue()) {
126       return CodeBlock.of("$LF", value.asFloat());
127     } else if (value.hasLongValue()) {
128       return CodeBlock.of("$LL", value.asLong());
129     } else if (value.hasShortValue()) {
130       return CodeBlock.of("(short) $L", value.asShort());
131     } else {
132       return CodeBlock.of("$L", value.getValue());
133     }
134   }
135 }
136