• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ImmutableSet;
20 import com.google.common.io.Resources;
21 
22 import java.io.IOException;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30 
31 import javax.lang.model.element.TypeElement;
32 import javax.lang.model.type.TypeKind;
33 import javax.lang.model.type.TypeMirror;
34 import javax.lang.model.util.Elements;
35 import javax.lang.model.util.Types;
36 
37 /**
38  * A collection of {@link MethodSignature} for accessible methods.
39  */
40 public final class Apis {
41 
42     private static final String[] API_FILES =
43             {"current.txt", "wifi-current.txt", "bluetooth-current.txt", "system-current.txt"};
44 
45     private static final Map<String, String> API_TXTS = initialiseApiTxts();
46     private static final Map<String, Apis> sPackageToApi = new HashMap<>();
47 
initialiseApiTxts()48     private static Map<String, String> initialiseApiTxts() {
49         return Arrays.stream(API_FILES)
50                 .collect(Collectors.toMap(f -> f, f -> {
51                     try {
52                         return Resources.toString(Processor.class.getResource("/apis/" + f),
53                                 StandardCharsets.UTF_8);
54                     } catch (IOException e) {
55                         throw new IllegalStateException("Could not read file " + f);
56                     }
57                 }));
58     }
59 
60     /**
61      * Get public and test APIs for a given class name.
62      */
63     public static Apis forClass(String className, Types types, Elements elements) {
64         if (sPackageToApi.containsKey(className)) {
65             return sPackageToApi.get(className);
66         }
67         ImmutableSet.Builder<MethodSignature> methods = ImmutableSet.builder();
68         Set<String> parents = new HashSet<>();
69         findParents(parents, className, elements);
70         for (String c : parents) {
71             for (Map.Entry<String, String> apiTxt : API_TXTS.entrySet()) {
72                 methods.addAll(
73                         parseApiTxt(apiTxt.getKey(), apiTxt.getValue(), c, types, elements));
74             }
75         }
76 
77         return new Apis(methods.build());
78     }
79 
80     private static void findParents(Set<String> parents, String className, Elements elements) {
81         parents.add(className);
82 
83         if (className.equals("java.lang.Object")) {
84             return;
85         }
86 
87         TypeElement element = elements.getTypeElement(className);
88         System.out.println("Checking " + className + " got " + element);
89 
90         TypeMirror superClass = element.getSuperclass();
91         if (!superClass.getKind().equals(TypeKind.NONE)) {
92             findParents(parents, superClass.toString(), elements);
93         }
94 
95         element.getInterfaces().stream().map(TypeMirror::toString)
96                 .forEach(c -> findParents(parents, c, elements));
97     }
98 
99     private static Set<MethodSignature> parseApiTxt(
100             String filename, String apiTxt, String className, Types types, Elements elements) {
101         System.out.println("Parsing for " + className);
102 
103         int separatorPosition = className.lastIndexOf(".");
104         String packageName = className.substring(0, separatorPosition);
105         String simpleClassName = className.substring(separatorPosition + 1);
106 
107         String[] packageSplit = apiTxt.split("package " + packageName + " \\{", 2);
108         if (packageSplit.length < 2) {
109             System.out.println("Package " + packageName + " not in file " + filename);
110             // Package not in this file
111             return new HashSet<>();
112         }
113         String[] classSplit = packageSplit[1].split("class " + simpleClassName + " .*?\n", 2);
114         if (classSplit.length < 2) {
115             System.out.println("Class " + simpleClassName + " not in file " + filename);
116             // Class not in this file
117             return new HashSet<>();
118         }
119         String[] lines = classSplit[1].split("\n");
120         Set<MethodSignature> methodSignatures = new HashSet<>();
121 
122         for (String line : lines) {
123             String methodLine = line.trim();
124             if (methodLine.isEmpty()) {
125                 continue;
126             }
127 
128             if (methodLine.startsWith("ctor")) {
129                 // Skip constructors
130                 continue;
131             }
132 
133             if (!methodLine.startsWith("method")) {
134                 return methodSignatures;
135             }
136 
137             try {
138                 // Strip "method" and semicolon
139                 methodLine = methodLine.substring(7, methodLine.length() - 1);
140                 MethodSignature signature =
141                         MethodSignature.forApiString(methodLine, types, elements);
142                 if (signature != null) {
143                     methodSignatures.add(signature);
144                 }
145             } catch (RuntimeException e) {
146                 throw new IllegalStateException("Error parsing method " + line, e);
147             }
148         }
149 
150         return methodSignatures;
151     }
152 
153     private final ImmutableSet<MethodSignature> mMethods;
154 
155     private Apis(ImmutableSet<MethodSignature> methods) {
156         mMethods = methods;
157     }
158 
159     /**
160      * Get methods in the API set.
161      */
162     public ImmutableSet<MethodSignature> methods() {
163         return mMethods;
164     }
165 }
166