blob: 4b5e09fc5bbb902e6ab0faea521abdc746b8bc8f [file] [log] [blame]
/*
* Copyright (C) 2015 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 android.databinding.tool.reflection;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
/**
* Class that is used for SDK related stuff.
* <p>
* Must be initialized with the sdk location to work properly
*/
public class SdkUtil {
static ApiChecker sApiChecker;
static int sMinSdk;
public static void initialize(int minSdk, File sdkPath) {
sMinSdk = minSdk;
sApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath()
+ "/platform-tools/api/api-versions.xml"));
L.d("SdkUtil init, minSdk: %s", minSdk);
}
public static int getMinApi(ModelClass modelClass) {
return sApiChecker.getMinApi(modelClass.getJniDescription(), null);
}
public static int getMinApi(ModelMethod modelMethod) {
ModelClass declaringClass = modelMethod.getDeclaringClass();
Preconditions.checkNotNull(sApiChecker, "should've initialized api checker");
while (declaringClass != null) {
String classDesc = declaringClass.getJniDescription();
String methodDesc = modelMethod.getJniDescription();
int result = sApiChecker.getMinApi(classDesc, methodDesc);
L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(),
classDesc, methodDesc, result);
if (result > 1) {
return result;
}
declaringClass = declaringClass.getSuperclass();
}
return 1;
}
static class ApiChecker {
private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
private Document mDoc;
private XPath mXPath;
public ApiChecker(File apiFile) {
InputStream inputStream = null;
try {
if (apiFile == null || !apiFile.exists()) {
inputStream = getClass().getClassLoader().getResourceAsStream("api-versions.xml");
} else {
inputStream = FileUtils.openInputStream(apiFile);
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
mDoc = builder.parse(inputStream);
XPathFactory xPathFactory = XPathFactory.newInstance();
mXPath = xPathFactory.newXPath();
buildFullLookup();
} catch (Throwable t) {
L.e(t, "cannot load api descriptions from %s", apiFile);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private void buildFullLookup() throws XPathExpressionException {
NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes();
for (int j = 0; j < allClasses.getLength(); j++) {
Node node = allClasses.item(j);
if (node.getNodeType() != Node.ELEMENT_NODE || !"class"
.equals(node.getNodeName())) {
continue;
}
//L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue());
int classSince = getSince(node);
String classDesc = node.getAttributes().getNamedItem("name").getNodeValue();
final NodeList childNodes = node.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE || !"method"
.equals(child.getNodeName())) {
continue;
}
int methodSince = getSince(child);
int since = Math.max(classSince, methodSince);
if (since > SdkUtil.sMinSdk) {
String methodDesc = child.getAttributes().getNamedItem("name")
.getNodeValue();
String key = cacheKey(classDesc, methodDesc);
mFullLookup.put(key, since);
}
}
}
}
public int getMinApi(String classDesc, String methodOrFieldDesc) {
if (mDoc == null || mXPath == null) {
return 1;
}
if (classDesc == null || classDesc.isEmpty()) {
return 1;
}
final String key = cacheKey(classDesc, methodOrFieldDesc);
Integer since = mFullLookup.get(key);
return since == null ? 1 : since;
}
private static String cacheKey(String classDesc, String methodOrFieldDesc) {
return classDesc + "~" + methodOrFieldDesc;
}
private static int getSince(Node node) {
final Node since = node.getAttributes().getNamedItem("since");
if (since != null) {
final String nodeValue = since.getNodeValue();
if (nodeValue != null && !nodeValue.isEmpty()) {
try {
return Integer.parseInt(nodeValue);
} catch (Throwable t) {
}
}
}
return 1;
}
}
}