1 /* 2 * Copyright (C) 2021 The Android Open Source Project 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 com.android.bedstead.remoteframeworkclasses.processor; 18 19 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.ImmutableSet; 21 22 import java.util.Arrays; 23 import java.util.HashSet; 24 import java.util.List; 25 import java.util.Objects; 26 import java.util.Set; 27 import java.util.stream.Collectors; 28 29 import javax.lang.model.element.Element; 30 import javax.lang.model.element.ExecutableElement; 31 import javax.lang.model.element.Modifier; 32 import javax.lang.model.element.TypeElement; 33 import javax.lang.model.type.DeclaredType; 34 import javax.lang.model.type.TypeKind; 35 import javax.lang.model.type.TypeMirror; 36 import javax.lang.model.util.Elements; 37 import javax.lang.model.util.Types; 38 39 /** 40 * Represents a minimal representation of a method for comparison purposes 41 */ 42 public final class MethodSignature { 43 44 /** Create a {@link MethodSignature} for the given {@link ExecutableElement}. */ forMethod(ExecutableElement method, Elements elements)45 public static MethodSignature forMethod(ExecutableElement method, Elements elements) { 46 List<TypeMirror> parameters = method.getParameters().stream() 47 .map(Element::asType) 48 .map(m -> rawType(m, elements)) 49 .collect(Collectors.toList()); 50 51 Set<TypeMirror> exceptions = method.getThrownTypes() 52 .stream().map(m -> rawType(m, elements)) 53 .collect(Collectors.toSet()); 54 55 return new MethodSignature(Visibility.ofMethod(method), 56 rawType(method.getReturnType(), elements), 57 method.getSimpleName().toString(), parameters, exceptions); 58 } 59 rawType(TypeMirror type, Elements elements)60 private static TypeMirror rawType(TypeMirror type, Elements elements) { 61 if (type instanceof DeclaredType) { 62 DeclaredType t = (DeclaredType) type; 63 if (!t.getTypeArguments().isEmpty()) { 64 type = elements.getTypeElement(t.toString().split("<", 2)[0]).asType(); 65 } 66 } 67 return type; 68 } 69 70 /** 71 * Create a {@link MethodSignature} for the given string from an API file. 72 */ forApiString( String string, Types types, Elements elements)73 public static /* @Nullable */ MethodSignature forApiString( 74 String string, Types types, Elements elements) { 75 // Strip annotations 76 string = string.replaceAll("@\\w+?\\(.+?\\) ", ""); 77 string = string.replaceAll("@.+? ", ""); 78 79 String[] parts = string.split(" ", 2); 80 Visibility visibility; 81 try { 82 visibility = Visibility.valueOf(parts[0].toUpperCase()); 83 } catch (IllegalArgumentException e) { 84 throw new IllegalStateException("Error finding visibility in string " + string); 85 } 86 string = parts[1]; 87 parts = string.split(" ", 2); 88 89 TypeMirror returnType; 90 while (parts[0].equals("abstract") || parts[0].equals("final") 91 || parts[0].equals("static")) { 92 // These don't affect the signature in ways we care about 93 string = parts[1]; 94 parts = string.split(" ", 2); 95 } 96 97 if (string.startsWith("<")) { 98 // This includes type arguments, for now we ignore this method 99 return null; 100 } 101 102 returnType = typeForString(parts[0], types, elements); 103 104 string = parts[1]; 105 parts = string.split("\\(", 2); 106 String methodName = parts[0]; 107 string = parts[1]; 108 parts = string.split("\\)", 2); 109 // Remove generic types as we don't need to care about them at this point 110 String parametersString = parts[0].replaceAll("<.*>", ""); 111 // Remove varargs 112 parametersString = parametersString.replaceAll("\\.\\.\\.", ""); 113 List<TypeMirror> parameters; 114 try { 115 parameters = Arrays.stream(parametersString.split(",")) 116 .map(String::trim) 117 .filter(t -> !t.isEmpty()) 118 .map(t -> typeForString(t, types, elements)) 119 .collect(Collectors.toList()); 120 } catch (IllegalStateException e) { 121 throw new IllegalStateException("Error parsing types from string " + parametersString); 122 } 123 string = parts[1]; 124 Set<TypeMirror> exceptions = new HashSet<>(); 125 if (string.contains("throws")) { 126 exceptions = Arrays.stream(string.split("throws ", 2)[1].split(",")) 127 .map(t -> t.trim()) 128 .filter(t -> !t.isEmpty()) 129 .map(t -> typeForString(t, types, elements)) 130 .collect(Collectors.toSet()); 131 } 132 133 return new MethodSignature(visibility, returnType, methodName, parameters, exceptions); 134 } 135 typeForString(String typeName, Types types, Elements elements)136 private static TypeMirror typeForString(String typeName, Types types, Elements elements) { 137 if (typeName.equals("void")) { 138 return types.getNoType(TypeKind.VOID); 139 } 140 141 if (typeName.contains("<")) { 142 // Because of type erasure we can just drop the type argument 143 return typeForString(typeName.split("<", 2)[0], types, elements); 144 } 145 146 if (typeName.endsWith("[]")) { 147 return types.getArrayType( 148 typeForString(typeName.substring(0, typeName.length() - 2), types, elements)); 149 } 150 151 try { 152 return types.getPrimitiveType(TypeKind.valueOf(typeName.toUpperCase())); 153 } catch (IllegalArgumentException e) { 154 // Not a primitive 155 } 156 157 TypeElement element = elements.getTypeElement(typeName); 158 if (element == null) { 159 // It could be java.lang 160 element = elements.getTypeElement("java.lang." + typeName); 161 } 162 163 if (element == null) { 164 throw new IllegalStateException("Unknown type: " + typeName); 165 } 166 167 return element.asType(); 168 } 169 170 enum Visibility { 171 PUBLIC, 172 PROTECTED; 173 ofMethod(ExecutableElement method)174 static Visibility ofMethod(ExecutableElement method) { 175 if (method.getModifiers().contains(Modifier.PUBLIC)) { 176 return PUBLIC; 177 } else if (method.getModifiers().contains(Modifier.PROTECTED)) { 178 return PROTECTED; 179 } 180 181 throw new IllegalArgumentException("Only public and protected are visible in APIs"); 182 } 183 } 184 185 private final Visibility mVisibility; 186 private final String mReturnType; 187 private final String mName; 188 private final ImmutableList<String> mParameterTypes; 189 private final ImmutableSet<String> mExceptions; MethodSignature( Visibility visibility, TypeMirror returnType, String name, List<TypeMirror> parameterTypes, Set<TypeMirror> exceptions)190 public MethodSignature( 191 Visibility visibility, TypeMirror returnType, String name, 192 List<TypeMirror> parameterTypes, Set<TypeMirror> exceptions) { 193 mVisibility = visibility; 194 mReturnType = returnType.toString(); 195 mName = name; 196 mParameterTypes = ImmutableList.copyOf(parameterTypes.stream() 197 .map(TypeMirror::toString) 198 .collect(Collectors.toList())); 199 mExceptions = ImmutableSet.copyOf(exceptions.stream().map(TypeMirror::toString).collect( 200 Collectors.toSet())); 201 } 202 203 @Override equals(Object o)204 public boolean equals(Object o) { 205 if (this == o) return true; 206 if (!(o instanceof MethodSignature)) return false; 207 MethodSignature that = (MethodSignature) o; 208 return mVisibility == that.mVisibility && mReturnType.equals(that.mReturnType) 209 && mName.equals( 210 that.mName) && mParameterTypes.equals(that.mParameterTypes) && mExceptions.equals( 211 that.mExceptions); 212 } 213 214 @Override hashCode()215 public int hashCode() { 216 return Objects.hash(mVisibility, mReturnType, mName, mParameterTypes, mExceptions); 217 } 218 219 @Override toString()220 public String toString() { 221 return "MethodSignature{" 222 + "mVisibility=" 223 + mVisibility 224 + ", mReturnType='" + mReturnType + '\'' 225 + ", mName='" + mName + '\'' 226 + ", mParameterTypes=" + mParameterTypes 227 + ", mExceptions=" + mExceptions 228 + '}'; 229 } 230 } 231