• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 androidx.room.compiler.processing.compat.XConverters.toXProcessing;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.collect.Iterables.getOnlyElement;
24 
25 import androidx.room.compiler.processing.XMethodElement;
26 import androidx.room.compiler.processing.XProcessingEnv;
27 import androidx.room.compiler.processing.XType;
28 import androidx.room.compiler.processing.XTypeElement;
29 import androidx.room.compiler.processing.compat.XConverters;
30 import com.squareup.javapoet.ClassName;
31 import com.squareup.javapoet.TypeName;
32 import java.util.Optional;
33 import javax.lang.model.SourceVersion;
34 import javax.lang.model.type.TypeKind;
35 
36 /** A utility class for {@link XProcessingEnvs} helper methods. */
37 // TODO(bcorso): Consider moving these methods into XProcessing library.
38 public final class XProcessingEnvs {
39   /** Returns {@code true} if the sources are being compiled on a javac and the version is <= 8. */
isPreJava8SourceVersion(XProcessingEnv processingEnv)40   public static boolean isPreJava8SourceVersion(XProcessingEnv processingEnv) {
41     Optional<SourceVersion> javaSourceVersion = javaSourceVersion(processingEnv);
42     return javaSourceVersion.isPresent()
43         && javaSourceVersion.get().compareTo(SourceVersion.RELEASE_8) < 0;
44   }
45 
46   /**
47    * Returns an optional containing the java source version if the current sources are being
48    * compiled with javac, or else returns an empty optional.
49    */
javaSourceVersion(XProcessingEnv processingEnv)50   private static Optional<SourceVersion> javaSourceVersion(XProcessingEnv processingEnv) {
51     switch (processingEnv.getBackend()) {
52       case JAVAC:
53         return Optional.of(toJavac(processingEnv).getSourceVersion());
54       case KSP:
55         return Optional.empty();
56     }
57     throw new AssertionError("Unexpected backend: " + processingEnv.getBackend());
58   }
59 
60   /**
61    * Returns {@code true} if {@code overrider} overrides {@code overridden} from within {@code type}
62    */
javacOverrides( XMethodElement overrider, XMethodElement overridden, XTypeElement type)63   public static boolean javacOverrides(
64       XMethodElement overrider,
65       XMethodElement overridden,
66       XTypeElement type) {
67     XProcessingEnv processingEnv = getProcessingEnv(type);
68     switch (processingEnv.getBackend()) {
69       case JAVAC:
70         // TODO(bcorso): Investigate why XMethodElement#overrides() throws exception in some cases.
71         return toJavac(processingEnv)
72             .getElementUtils() // ALLOW_TYPES_ELEMENTS
73             .overrides(toJavac(overrider), toJavac(overridden), toJavac(type));
74       case KSP:
75         // For KSP, just use the standard overrides since the issues above are specific to javac.
76         return overrider.overrides(overridden, type);
77     }
78     throw new AssertionError("Unexpected backend: " + processingEnv.getBackend());
79   }
80 
81   /** Returns a new unbounded wildcard type argument, i.e. {@code <?>}. */
getUnboundedWildcardType(XProcessingEnv processingEnv)82   public static XType getUnboundedWildcardType(XProcessingEnv processingEnv) {
83     switch (processingEnv.getBackend()) {
84       case JAVAC:
85         return toXProcessing(
86             toJavac(processingEnv)
87                 .getTypeUtils() // ALLOW_TYPES_ELEMENTS
88                 .getWildcardType(null, null),
89             processingEnv);
90       case KSP:
91         // In KSP, the equivalent of an unbounded wildcard type is the star projection. However,
92         // there's currently no way to create a star projection type directly. Instead, we create a
93         // List<T> type, get its star projection, and then grab the type argument from that.
94         return XConverters.toXProcessing(
95                 XConverters.toKS(processingEnv.requireType("java.util.List")).starProjection(),
96                 processingEnv)
97             .getTypeArguments()
98             .get(0);
99     }
100     throw new AssertionError("Unexpected backend: " + processingEnv.getBackend());
101   }
102 
103   /**
104    * Returns {@code type}'s single type argument.
105    *
106    * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
107    *
108    * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
109    *     than one type arguments.
110    */
unwrapType(XType type)111   public static XType unwrapType(XType type) {
112     XType unwrapped = unwrapTypeOrDefault(type, null);
113     checkArgument(unwrapped != null, "%s is a raw type", type);
114     return unwrapped;
115   }
116 
117   /**
118    * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not.
119    *
120    * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
121    *
122    * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
123    *     type argument.
124    */
unwrapTypeOrObject(XType type, XProcessingEnv processingEnv)125   public static XType unwrapTypeOrObject(XType type, XProcessingEnv processingEnv) {
126     return unwrapTypeOrDefault(type, processingEnv.requireType(TypeName.OBJECT));
127   }
128 
unwrapTypeOrDefault(XType type, XType defaultType)129   private static XType unwrapTypeOrDefault(XType type, XType defaultType) {
130     XTypeElement typeElement = type.getTypeElement();
131     checkArgument(
132         !typeElement.getType().getTypeArguments().isEmpty(),
133         "%s does not have a type parameter",
134         typeElement.getQualifiedName());
135     return getOnlyElement(type.getTypeArguments(), defaultType);
136   }
137 
138   /** Returns a primitive int {@link XType}. */
getPrimitiveIntType(XProcessingEnv processingEnv)139   public static XType getPrimitiveIntType(XProcessingEnv processingEnv) {
140     return toXProcessing(
141         toJavac(processingEnv)
142             .getTypeUtils() // ALLOW_TYPES_ELEMENTS
143             .getPrimitiveType(TypeKind.INT),
144         processingEnv);
145   }
146 
147   /** Returns the type this method is enclosed in. */
wrapType(ClassName wrapper, XType type, XProcessingEnv processingEnv)148   public static XType wrapType(ClassName wrapper, XType type, XProcessingEnv processingEnv) {
149     return processingEnv.getDeclaredType(processingEnv.requireTypeElement(wrapper), type);
150   }
151 
XProcessingEnvs()152   private XProcessingEnvs() {}
153 }
154