blob: 550f1d3245f53ed9b926f260473696b5d8be3924 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 com.android.cts.managedprofile;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Primitives;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.lang.reflect.Method;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Helper class for retrieving the current list of API methods.
*/
public class CurrentApiHelper {
/**
* Location of the XML file that lists the current public APIs.
*
* <p><b>Note:</b> must be consistent with
* {@code cts/hostsidetests/devicepolicy/AndroidTest.xml}
*/
private static final String CURRENT_API_FILE = "/data/local/tmp/device-policy-test/current.api";
private static final String LOG_TAG = "CurrentApiHelper";
private static final ImmutableMap<String, Class> PRIMITIVE_TYPES = getPrimitiveTypes();
private static final ImmutableMap<String, String> PRIMITIVE_ENCODINGS =
new ImmutableMap.Builder<String, String>()
.put("boolean", "Z")
.put("byte", "B")
.put("char", "C")
.put("double", "D")
.put("float", "F")
.put("int", "I")
.put("long", "J")
.put("short", "S")
.build();
private static final String TAG_PACKAGE = "package";
private static final String TAG_CLASS = "class";
private static final String TAG_METHOD = "method";
private static final String TAG_PARAMETER = "parameter";
private static final String ATTRIBUTE_NAME = "name";
private static final String ATTRIBUTE_TYPE = "type";
/**
* Get public API methods of a specific class as defined in the API document.
*
* @param packageName The name of the package containing the class, e.g. {@code android.app}.
* @param className The name of the class, e.g. {@code Application}.
* @return an immutable list of {@link Method} instances.
*/
public static ImmutableList<Method> getPublicApis(String packageName, String className)
throws Exception {
Document apiDocument = parseXmlFile(CURRENT_API_FILE);
Element rootElement = apiDocument.getDocumentElement();
Element packageElement = getChildElementByName(rootElement, TAG_PACKAGE, packageName);
Element classElement = getChildElementByName(packageElement, TAG_CLASS, className);
ImmutableList.Builder<Method> builder = new ImmutableList.Builder<>();
NodeList nodes = classElement.getElementsByTagName(TAG_METHOD);
if (nodes != null && nodes.getLength() > 0) {
Class clazz = Class.forName(packageName + "." + className);
for (int i = 0; i < nodes.getLength(); ++i) {
Element element = (Element) nodes.item(i);
String name = element.getAttribute(ATTRIBUTE_NAME);
Class[] paramTypes = getParamTypes(element);
builder.add(clazz.getMethod(name, paramTypes));
}
}
return builder.build();
}
/**
* Given a {@link Class} object, get the default value if the {@link Class} refers to a
* primitive type, or null if it refers to an object.
*
* <p><ul>
* <li>For boolean type, return {@code false}
* <li>For other primitive types, return {@code 0}
* <li>For all other types, return {@code null}
* </ul>
* @param clazz The desired class to instantiate.
* @return Default instance as described above.
*/
public static Object instantiate(Class clazz) {
if (clazz.isPrimitive()) {
if (boolean.class.equals(clazz)) {
return false;
} else {
return 0;
}
} else {
return null;
}
}
private static Document parseXmlFile(String filePath) throws Exception {
File apiFile = new File(filePath);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse(apiFile.toURI().toString());
return dom;
}
private static Element getChildElementByName(Element parent,
String childTag, String childName) {
NodeList nodeList = parent.getElementsByTagName(childTag);
if (nodeList != null && nodeList.getLength() > 0) {
for (int i = 0; i < nodeList.getLength(); ++i) {
Element el = (Element) nodeList.item(i);
if (childName.equals(el.getAttribute(ATTRIBUTE_NAME))) {
return el;
}
}
}
return null;
}
private static Class[] getParamTypes(Element methodElement) throws Exception {
NodeList nodes = methodElement.getElementsByTagName(TAG_PARAMETER);
if (nodes != null && nodes.getLength() > 0) {
int paramCount = nodes.getLength();
Class[] paramTypes = new Class[paramCount];
for (int i = 0; i < paramCount; ++i) {
String typeName = ((Element) nodes.item(i)).getAttribute(ATTRIBUTE_TYPE);
paramTypes[i] = getClassByName(typeName);
}
return paramTypes;
} else {
return new Class[0];
}
}
private static Class getClassByName(String typeName) throws ClassNotFoundException {
// Check if typeName represents an array
int arrayDim = 0;
while (typeName.endsWith("[]")) {
arrayDim++;
typeName = typeName.substring(0, typeName.length() - 2);
}
// Remove type parameters, if any
typeName = typeName.replaceAll("<.*>$", "");
if (arrayDim == 0) {
if (isPrimitiveTypeName(typeName)) {
return PRIMITIVE_TYPES.get(typeName);
} else {
return Class.forName(typeName);
}
} else {
String prefix = Strings.repeat("[", arrayDim);
if (isPrimitiveTypeName(typeName)) {
return Class.forName(prefix + PRIMITIVE_ENCODINGS.get(typeName));
} else {
return Class.forName(prefix + "L" + typeName + ";");
}
}
}
private static ImmutableMap<String, Class> getPrimitiveTypes() {
ImmutableMap.Builder<String, Class> builder = new ImmutableMap.Builder<>();
for (Class type : Primitives.allPrimitiveTypes()) {
builder.put(type.getName(), type);
}
return builder.build();
}
private static boolean isPrimitiveTypeName(String typeName) {
return PRIMITIVE_TYPES.containsKey(typeName);
}
}