1 /*
2  * Copyright (C) 2018 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.cts.releaseparser;
18 
19 import com.android.cts.releaseparser.ReleaseProto.*;
20 
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.nio.charset.Charset;
29 import java.util.HashMap;
30 import java.util.Map;
31 
32 public class ClassUtils {
33     public static final char PACKAGE_SEPARATOR = '/';
34     public static final char INNER_CLASS_SEPARATOR = '$';
35     public static final char SPECIAL_CLASS_CHARACTER = '-';
36     public static final char SPECIAL_MEMBER_SEPARATOR = '$';
37     public static final char CLASS_MEMBER_SELECTOR = '.';
38 
39     public static final char TYPE_VOID = 'V';
40     public static final char TYPE_BOOLEAN = 'Z';
41     public static final char TYPE_BYTE = 'B';
42     public static final char TYPE_CHAR = 'C';
43     public static final char TYPE_SHORT = 'S';
44     public static final char TYPE_INT = 'I';
45     public static final char TYPE_LONG = 'J';
46     public static final char TYPE_FLOAT = 'F';
47     public static final char TYPE_DOUBLE = 'D';
48     public static final char TYPE_CLASS_START = 'L';
49     public static final char TYPE_CLASS_END = ';';
50     public static final char TYPE_ARRAY = '[';
51     public static final String TYPE_ARRAY_ACCESS = "[]";
52 
53     private static Map<Character, String> primitiveTypes = new HashMap<>();
54 
55     static {
primitiveTypes.put(TYPE_VOID, "void")56         primitiveTypes.put(TYPE_VOID, "void");
primitiveTypes.put(TYPE_BOOLEAN, "boolean")57         primitiveTypes.put(TYPE_BOOLEAN, "boolean");
primitiveTypes.put(TYPE_BYTE, "byte")58         primitiveTypes.put(TYPE_BYTE, "byte");
primitiveTypes.put(TYPE_CHAR, "char")59         primitiveTypes.put(TYPE_CHAR, "char");
primitiveTypes.put(TYPE_SHORT, "short")60         primitiveTypes.put(TYPE_SHORT, "short");
primitiveTypes.put(TYPE_INT, "int")61         primitiveTypes.put(TYPE_INT, "int");
primitiveTypes.put(TYPE_LONG, "long")62         primitiveTypes.put(TYPE_LONG, "long");
primitiveTypes.put(TYPE_FLOAT, "float")63         primitiveTypes.put(TYPE_FLOAT, "float");
primitiveTypes.put(TYPE_DOUBLE, "double")64         primitiveTypes.put(TYPE_DOUBLE, "double");
65     }
66 
67     /**
68      * Gets canonical name of a class
69      *
70      * @param name such as: [[Lcom/foo/bar/MyClass$Inner;
71      * @return canonical name as: com.foo.bar.MyClass.Inner[][]
72      */
getCanonicalName(String name)73     public static String getCanonicalName(String name) {
74         int arrDimension = 0;
75         for (int i = 0; i < name.length(); i++) {
76             if (name.charAt(i) == TYPE_ARRAY) {
77                 arrDimension++;
78             } else {
79                 break;
80             }
81         }
82 
83         // test the first character.
84         final char firstChar = name.charAt(arrDimension);
85         if (primitiveTypes.containsKey(firstChar)) {
86             name = primitiveTypes.get(firstChar);
87         } else if (firstChar == TYPE_CLASS_START) {
88             // omit the leading 'L' and the trailing ';'
89             name = name.substring(arrDimension + 1, name.length() - 1);
90 
91             // replace '/' and '$' to '.'
92             name =
93                     name.replace(PACKAGE_SEPARATOR, CLASS_MEMBER_SELECTOR)
94                             .replace(INNER_CLASS_SEPARATOR, CLASS_MEMBER_SELECTOR);
95         }
96 
97         // add []'s, if any
98         for (int i = 0; i < arrDimension; i++) {
99             name += TYPE_ARRAY_ACCESS;
100         }
101 
102         return name;
103     }
104 
105     /**
106      * Reads a file from Resource and write to a tmp file
107      *
108      * @param clazz the Class contins resrouce files
109      * @param fileName of a resource file
110      * @return the File object of a tmp file
111      */
getResrouceFile(Class clazz, String fileName)112     public static File getResrouceFile(Class clazz, String fileName) throws IOException {
113         File tempFile = File.createTempFile(fileName, "");
114         tempFile.deleteOnExit();
115         try (InputStream input = openResourceAsStream(clazz, fileName);
116                 OutputStream output = new FileOutputStream(tempFile)) {
117             byte[] buffer = new byte[4096];
118             int length;
119             while ((length = input.read(buffer)) > 0) {
120                 output.write(buffer, 0, length);
121             }
122         }
123         return tempFile;
124     }
125 
126     /**
127      * Gets Resource file content as a String
128      *
129      * @param clazz the Class contins resrouce files
130      * @param fileName of a resource file
131      * @return a String of the resrouce file content
132      */
getResrouceContentString(Class clazz, String fileName)133     public static String getResrouceContentString(Class clazz, String fileName) throws IOException {
134         InputStream inStream = openResourceAsStream(clazz, fileName);
135         StringBuilder stringBuilder = new StringBuilder();
136         String line = null;
137 
138         try (BufferedReader bufferedReader =
139                 new BufferedReader(new InputStreamReader(inStream, Charset.forName("UTF-8")))) {
140             while ((line = bufferedReader.readLine()) != null) {
141                 stringBuilder.append(line);
142             }
143         }
144 
145         return stringBuilder.toString();
146     }
147 
148     /**
149      * Gets an InputStrem of a file from Resource
150      *
151      * @param clazz the Class contins resrouce files
152      * @param fileName of a resource file
153      * @return the (@link InputStream} object of the file
154      */
openResourceAsStream(Class clazz, String fileName)155     public static InputStream openResourceAsStream(Class clazz, String fileName) {
156         InputStream input = clazz.getResourceAsStream("/" + fileName);
157         return input;
158     }
159 
160     /**
161      * Gets an InputStremReader of a file from Resource
162      *
163      * @param clazz the Class contins resrouce files
164      * @param fileName of a resource file
165      * @return the (@link InputStream} object of the file
166      */
openResourceAsStreamReader(Class clazz, String fileName)167     public static InputStreamReader openResourceAsStreamReader(Class clazz, String fileName) {
168         return new InputStreamReader(
169                 openResourceAsStream(clazz, fileName), Charset.forName("UTF-8"));
170     }
171 
172     /**
173      * Gets/new ApiClass.Builder from a map by the class name
174      *
175      * @param HashMap<class name, ApiClass.Builder>
176      * @param Class name
177      * @return ApiClass.Builder
178      */
getApiClassBuilder( HashMap<String, ApiClass.Builder> apiClassBuilderMap, String name)179     public static ApiClass.Builder getApiClassBuilder(
180             HashMap<String, ApiClass.Builder> apiClassBuilderMap, String name) {
181         ApiClass.Builder builder = apiClassBuilderMap.get(name);
182         if (builder == null) {
183             builder = ApiClass.newBuilder().setName(ClassUtils.getCanonicalName(name));
184             apiClassBuilderMap.put(name, builder);
185         }
186         return builder;
187     }
188 
189     /**
190      * Adds all ApiClass in a map to ApiPackage.Builder
191      *
192      * @param HashMap<class name, ApiClass.Builder>
193      * @param ApiPackage.Builder
194      */
addAllApiClasses( HashMap<String, ApiClass.Builder> apiClassBuilderMap, ApiPackage.Builder apiPackageBuilder)195     public static void addAllApiClasses(
196             HashMap<String, ApiClass.Builder> apiClassBuilderMap,
197             ApiPackage.Builder apiPackageBuilder) {
198         apiClassBuilderMap
199                 .values()
200                 .forEach(
201                         value -> {
202                             apiPackageBuilder.addClasses(value.build());
203                         });
204     }
205 }
206