1 /* 2 * Copyright 2021 Google LLC 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 * https://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 package com.google.android.enterprise.connectedapps.processor; 17 18 import static com.google.android.enterprise.connectedapps.processor.CommonClassNames.BUNDLER_TYPE_CLASSNAME; 19 import static java.util.stream.Collectors.toList; 20 21 import com.squareup.javapoet.ClassName; 22 import com.squareup.javapoet.CodeBlock; 23 import java.util.ArrayList; 24 import java.util.List; 25 import javax.lang.model.type.ArrayType; 26 import javax.lang.model.type.DeclaredType; 27 import javax.lang.model.type.TypeMirror; 28 29 /** Utilities for manipulating {@link TypeMirror} instances. */ 30 public class TypeUtils { 31 isArray(TypeMirror type)32 public static boolean isArray(TypeMirror type) { 33 return type instanceof ArrayType; 34 } 35 36 /** 37 * Extract a type from an array. 38 * 39 * <p>Assumes that {@code type} represents an array. 40 */ extractTypeFromArray(TypeMirror type)41 public static TypeMirror extractTypeFromArray(TypeMirror type) { 42 return ((ArrayType) type).getComponentType(); 43 } 44 isGeneric(TypeMirror type)45 public static boolean isGeneric(TypeMirror type) { 46 if (type instanceof DeclaredType) { 47 return !((DeclaredType) type).getTypeArguments().isEmpty(); 48 } 49 return false; 50 } 51 removeTypeArguments(TypeMirror type)52 public static TypeMirror removeTypeArguments(TypeMirror type) { 53 if (type instanceof DeclaredType) { 54 return ((DeclaredType) type).asElement().asType(); 55 } 56 return type; 57 } 58 extractTypeArguments(TypeMirror type)59 public static List<TypeMirror> extractTypeArguments(TypeMirror type) { 60 if (!(type instanceof DeclaredType)) { 61 return null; 62 } 63 64 return new ArrayList<>(((DeclaredType) type).getTypeArguments()); 65 } 66 getRawTypeClassName(TypeMirror type)67 static ClassName getRawTypeClassName(TypeMirror type) { 68 String rawTypeQualifiedName = getRawTypeQualifiedName(type); 69 70 if (!rawTypeQualifiedName.contains(".")) { 71 return ClassName.get("", rawTypeQualifiedName); 72 } 73 74 String packageName = rawTypeQualifiedName.substring(0, rawTypeQualifiedName.lastIndexOf(".")); 75 String simpleName = rawTypeQualifiedName.substring(rawTypeQualifiedName.lastIndexOf(".") + 1); 76 77 return ClassName.get(packageName, simpleName); 78 } 79 getRawTypeQualifiedName(TypeMirror type)80 static String getRawTypeQualifiedName(TypeMirror type) { 81 // This converts e.g. java.util.List<String> into java.util.List 82 return type.toString().split("<", 2)[0]; 83 } 84 generateBundlerType(TypeMirror type)85 static CodeBlock generateBundlerType(TypeMirror type) { 86 if (isArray(type)) { 87 return generateArrayBundlerType(type); 88 } 89 if (isGeneric(type)) { 90 return generateGenericBundlerType(type); 91 } 92 return CodeBlock.of("$T.of($S)", BUNDLER_TYPE_CLASSNAME, getRawTypeQualifiedName(type)); 93 } 94 getArrayRootType(TypeMirror type)95 static TypeMirror getArrayRootType(TypeMirror type) { 96 while (TypeUtils.isArray(type)) { 97 type = TypeUtils.extractTypeFromArray(type); 98 } 99 100 return type; 101 } 102 isPrimitiveArray(TypeMirror type)103 static boolean isPrimitiveArray(TypeMirror type) { 104 return getArrayRootType(type).getKind().isPrimitive(); 105 } 106 generateArrayBundlerType(TypeMirror type)107 private static CodeBlock generateArrayBundlerType(TypeMirror type) { 108 TypeMirror arrayType = extractTypeFromArray(type); 109 110 if (isPrimitiveArray(arrayType)) { 111 return CodeBlock.of("$T.of($S)", BUNDLER_TYPE_CLASSNAME, type); 112 } 113 114 return CodeBlock.of( 115 "$T.of($S, $L)", 116 BUNDLER_TYPE_CLASSNAME, 117 "java.lang.Object[]", 118 generateBundlerType(arrayType)); 119 } 120 generateGenericBundlerType(TypeMirror type)121 private static CodeBlock generateGenericBundlerType(TypeMirror type) { 122 CodeBlock.Builder typeArgs = CodeBlock.builder(); 123 124 List<CodeBlock> typeArgBlocks = 125 extractTypeArguments(type).stream().map(TypeUtils::generateBundlerType).collect(toList()); 126 127 typeArgs.add(typeArgBlocks.get(0)); 128 for (CodeBlock typeArgBlock : typeArgBlocks.subList(1, typeArgBlocks.size())) { 129 typeArgs.add(", $L", typeArgBlock); 130 } 131 132 return CodeBlock.of( 133 "$T.of($S, $L)", BUNDLER_TYPE_CLASSNAME, getRawTypeQualifiedName(type), typeArgs.build()); 134 } 135 TypeUtils()136 private TypeUtils() {} 137 } 138