blob: 177935ab7f73853c7a47dd8663a7c910abdfaa5f [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 com.google.common.base.Preconditions;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import android.databinding.tool.util.L;
import java.io.File;
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 File mSdkPath;
static ApiChecker mApiChecker;
static int mMinSdk;
public static void initialize(int minSdk, File sdkPath) {
mSdkPath = sdkPath;
mMinSdk = minSdk;
mApiChecker = 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 mApiChecker.getMinApi(modelClass.getJniDescription(), null);
}
public static int getMinApi(ModelMethod modelMethod) {
ModelClass declaringClass = modelMethod.getDeclaringClass();
Preconditions.checkNotNull(mApiChecker, "should've initialized api checker");
while (declaringClass != null) {
String classDesc = declaringClass.getJniDescription();
String methodDesc = modelMethod.getJniDescription();
int result = mApiChecker.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;
}
private static class ApiChecker {
private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
private Document mDoc;
private XPath mXPath;
public ApiChecker(File apiFile) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
mDoc = builder.parse(apiFile);
XPathFactory xPathFactory = XPathFactory.newInstance();
mXPath = xPathFactory.newXPath();
buildFullLookup();
} catch (Throwable t) {
L.e(t, "cannot load api descriptions from %s", apiFile);
}
}
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.mMinSdk) {
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;
}
}
}