/* * Copyright (C) 2022 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.xprocessing; import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv; import static androidx.room.compiler.processing.compat.XConverters.toJavac; import static androidx.room.compiler.processing.compat.XConverters.toXProcessing; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.getOnlyElement; import androidx.room.compiler.processing.XMethodElement; import androidx.room.compiler.processing.XProcessingEnv; import androidx.room.compiler.processing.XType; import androidx.room.compiler.processing.XTypeElement; import androidx.room.compiler.processing.compat.XConverters; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import java.util.Optional; import javax.lang.model.SourceVersion; import javax.lang.model.type.TypeKind; /** A utility class for {@link XProcessingEnvs} helper methods. */ // TODO(bcorso): Consider moving these methods into XProcessing library. public final class XProcessingEnvs { /** Returns {@code true} if the sources are being compiled on a javac and the version is <= 8. */ public static boolean isPreJava8SourceVersion(XProcessingEnv processingEnv) { Optional javaSourceVersion = javaSourceVersion(processingEnv); return javaSourceVersion.isPresent() && javaSourceVersion.get().compareTo(SourceVersion.RELEASE_8) < 0; } /** * Returns an optional containing the java source version if the current sources are being * compiled with javac, or else returns an empty optional. */ private static Optional javaSourceVersion(XProcessingEnv processingEnv) { switch (processingEnv.getBackend()) { case JAVAC: return Optional.of(toJavac(processingEnv).getSourceVersion()); case KSP: return Optional.empty(); } throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); } /** * Returns {@code true} if {@code overrider} overrides {@code overridden} from within {@code type} */ public static boolean javacOverrides( XMethodElement overrider, XMethodElement overridden, XTypeElement type) { XProcessingEnv processingEnv = getProcessingEnv(type); switch (processingEnv.getBackend()) { case JAVAC: // TODO(bcorso): Investigate why XMethodElement#overrides() throws exception in some cases. return toJavac(processingEnv) .getElementUtils() // ALLOW_TYPES_ELEMENTS .overrides(toJavac(overrider), toJavac(overridden), toJavac(type)); case KSP: // For KSP, just use the standard overrides since the issues above are specific to javac. return overrider.overrides(overridden, type); } throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); } /** Returns a new unbounded wildcard type argument, i.e. {@code }. */ public static XType getUnboundedWildcardType(XProcessingEnv processingEnv) { switch (processingEnv.getBackend()) { case JAVAC: return toXProcessing( toJavac(processingEnv) .getTypeUtils() // ALLOW_TYPES_ELEMENTS .getWildcardType(null, null), processingEnv); case KSP: // In KSP, the equivalent of an unbounded wildcard type is the star projection. However, // there's currently no way to create a star projection type directly. Instead, we create a // List type, get its star projection, and then grab the type argument from that. return XConverters.toXProcessing( XConverters.toKS(processingEnv.requireType("java.util.List")).starProjection(), processingEnv) .getTypeArguments() .get(0); } throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); } /** * Returns {@code type}'s single type argument. * *

For example, if {@code type} is {@code List} this will return {@code Number}. * * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more * than one type arguments. */ public static XType unwrapType(XType type) { XType unwrapped = unwrapTypeOrDefault(type, null); checkArgument(unwrapped != null, "%s is a raw type", type); return unwrapped; } /** * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not. * *

For example, if {@code type} is {@code List} this will return {@code Number}. * * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one * type argument. */ public static XType unwrapTypeOrObject(XType type, XProcessingEnv processingEnv) { return unwrapTypeOrDefault(type, processingEnv.requireType(TypeName.OBJECT)); } private static XType unwrapTypeOrDefault(XType type, XType defaultType) { XTypeElement typeElement = type.getTypeElement(); checkArgument( !typeElement.getType().getTypeArguments().isEmpty(), "%s does not have a type parameter", typeElement.getQualifiedName()); return getOnlyElement(type.getTypeArguments(), defaultType); } /** Returns a primitive int {@link XType}. */ public static XType getPrimitiveIntType(XProcessingEnv processingEnv) { return toXProcessing( toJavac(processingEnv) .getTypeUtils() // ALLOW_TYPES_ELEMENTS .getPrimitiveType(TypeKind.INT), processingEnv); } /** Returns the type this method is enclosed in. */ public static XType wrapType(ClassName wrapper, XType type, XProcessingEnv processingEnv) { return processingEnv.getDeclaredType(processingEnv.requireTypeElement(wrapper), type); } private XProcessingEnvs() {} }