197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar/* 297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * you may not use this file except in compliance with the License. 597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * You may obtain a copy of the License at 697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Unless required by applicable law or agreed to in writing, software 897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * See the License for the specific language governing permissions and 1197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * limitations under the License. 1297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar */ 1397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 14fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.reflection; 1597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 16ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyarimport org.apache.commons.io.FileUtils; 17ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyarimport org.apache.commons.io.IOUtils; 1897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport org.w3c.dom.Document; 1997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport org.w3c.dom.Node; 2097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport org.w3c.dom.NodeList; 21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount 22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L; 232611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyarimport android.databinding.tool.util.Preconditions; 24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount 2597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.io.File; 26ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyarimport java.io.InputStream; 2797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.HashMap; 2897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.Map; 2997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 3097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.parsers.DocumentBuilder; 3197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.parsers.DocumentBuilderFactory; 3297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPath; 3397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPathExpressionException; 3497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPathFactory; 3597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 3697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar/** 3797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Class that is used for SDK related stuff. 3897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * <p> 3997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Must be initialized with the sdk location to work properly 4097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar */ 4197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarpublic class SdkUtil { 4297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 43ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar static ApiChecker sApiChecker; 4497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 45ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar static int sMinSdk; 4697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 4797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar public static void initialize(int minSdk, File sdkPath) { 48ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar sMinSdk = minSdk; 49ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar sApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath() 5097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar + "/platform-tools/api/api-versions.xml")); 5197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar L.d("SdkUtil init, minSdk: %s", minSdk); 5297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 5397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 5497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar public static int getMinApi(ModelClass modelClass) { 55ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar return sApiChecker.getMinApi(modelClass.getJniDescription(), null); 5697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 5797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 5897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar public static int getMinApi(ModelMethod modelMethod) { 5997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar ModelClass declaringClass = modelMethod.getDeclaringClass(); 60ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar Preconditions.checkNotNull(sApiChecker, "should've initialized api checker"); 6197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar while (declaringClass != null) { 6297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar String classDesc = declaringClass.getJniDescription(); 6397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar String methodDesc = modelMethod.getJniDescription(); 64ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar int result = sApiChecker.getMinApi(classDesc, methodDesc); 6597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(), 6697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar classDesc, methodDesc, result); 6797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (result > 1) { 6897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return result; 6997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 7097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar declaringClass = declaringClass.getSuperclass(); 7197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 7297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return 1; 7397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 7497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 75ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar static class ApiChecker { 7697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 77895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar private Map<String, Integer> mFullLookup = new HashMap<String, Integer>(); 7897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 7997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar private Document mDoc; 8097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 8197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar private XPath mXPath; 8297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 8397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar public ApiChecker(File apiFile) { 84ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar InputStream inputStream = null; 8597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar try { 86ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar if (apiFile == null || !apiFile.exists()) { 87ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar inputStream = getClass().getClassLoader().getResourceAsStream("api-versions.xml"); 88ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar } else { 89ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar inputStream = FileUtils.openInputStream(apiFile); 90ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar } 9197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 9297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar DocumentBuilder builder = factory.newDocumentBuilder(); 93ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar mDoc = builder.parse(inputStream); 9497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar XPathFactory xPathFactory = XPathFactory.newInstance(); 9597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar mXPath = xPathFactory.newXPath(); 9697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar buildFullLookup(); 9797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } catch (Throwable t) { 9897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar L.e(t, "cannot load api descriptions from %s", apiFile); 99ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar } finally { 100ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar IOUtils.closeQuietly(inputStream); 10197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 10297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 10397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 10497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar private void buildFullLookup() throws XPathExpressionException { 10597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes(); 10697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar for (int j = 0; j < allClasses.getLength(); j++) { 10797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar Node node = allClasses.item(j); 10897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (node.getNodeType() != Node.ELEMENT_NODE || !"class" 10997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar .equals(node.getNodeName())) { 11097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar continue; 11197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 11297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue()); 11397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar int classSince = getSince(node); 11497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar String classDesc = node.getAttributes().getNamedItem("name").getNodeValue(); 11597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 11697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar final NodeList childNodes = node.getChildNodes(); 11797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar for (int i = 0; i < childNodes.getLength(); i++) { 11897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar Node child = childNodes.item(i); 11997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (child.getNodeType() != Node.ELEMENT_NODE || !"method" 12097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar .equals(child.getNodeName())) { 12197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar continue; 12297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 12397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar int methodSince = getSince(child); 12497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar int since = Math.max(classSince, methodSince); 125ee7586713d68806b556a425cbebf007a56261ff3Yigit Boyar if (since > SdkUtil.sMinSdk) { 12697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar String methodDesc = child.getAttributes().getNamedItem("name") 12797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar .getNodeValue(); 12897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar String key = cacheKey(classDesc, methodDesc); 12997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar mFullLookup.put(key, since); 13097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 13197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 13297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 13397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 13497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 13597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar public int getMinApi(String classDesc, String methodOrFieldDesc) { 13697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (mDoc == null || mXPath == null) { 13797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return 1; 13897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 13997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (classDesc == null || classDesc.isEmpty()) { 14097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return 1; 14197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 14297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar final String key = cacheKey(classDesc, methodOrFieldDesc); 14397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar Integer since = mFullLookup.get(key); 14497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return since == null ? 1 : since; 14597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 14697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 14797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar private static String cacheKey(String classDesc, String methodOrFieldDesc) { 14897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return classDesc + "~" + methodOrFieldDesc; 14997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 15097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 15197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar private static int getSince(Node node) { 15297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar final Node since = node.getAttributes().getNamedItem("since"); 15397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (since != null) { 15497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar final String nodeValue = since.getNodeValue(); 15597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar if (nodeValue != null && !nodeValue.isEmpty()) { 15697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar try { 15797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return Integer.parseInt(nodeValue); 15897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } catch (Throwable t) { 15997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 16097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 16197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 16297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar 16397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar return 1; 16497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 16597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar } 16697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar} 167