1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14 package android.databinding.tool.reflection; 15 16 import org.apache.commons.io.FileUtils; 17 import org.apache.commons.io.IOUtils; 18 import org.w3c.dom.Document; 19 import org.w3c.dom.Node; 20 import org.w3c.dom.NodeList; 21 22 import android.databinding.tool.util.L; 23 import android.databinding.tool.util.Preconditions; 24 25 import java.io.File; 26 import java.io.InputStream; 27 import java.util.HashMap; 28 import java.util.Map; 29 30 import javax.xml.parsers.DocumentBuilder; 31 import javax.xml.parsers.DocumentBuilderFactory; 32 import javax.xml.xpath.XPath; 33 import javax.xml.xpath.XPathExpressionException; 34 import javax.xml.xpath.XPathFactory; 35 36 /** 37 * Class that is used for SDK related stuff. 38 * <p> 39 * Must be initialized with the sdk location to work properly 40 */ 41 public class SdkUtil { 42 43 static ApiChecker sApiChecker; 44 45 static int sMinSdk; 46 initialize(int minSdk, File sdkPath)47 public static void initialize(int minSdk, File sdkPath) { 48 sMinSdk = minSdk; 49 sApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath() 50 + "/platform-tools/api/api-versions.xml")); 51 L.d("SdkUtil init, minSdk: %s", minSdk); 52 } 53 getMinApi(ModelClass modelClass)54 public static int getMinApi(ModelClass modelClass) { 55 return sApiChecker.getMinApi(modelClass.getJniDescription(), null); 56 } 57 getMinApi(ModelMethod modelMethod)58 public static int getMinApi(ModelMethod modelMethod) { 59 ModelClass declaringClass = modelMethod.getDeclaringClass(); 60 Preconditions.checkNotNull(sApiChecker, "should've initialized api checker"); 61 while (declaringClass != null) { 62 String classDesc = declaringClass.getJniDescription(); 63 String methodDesc = modelMethod.getJniDescription(); 64 int result = sApiChecker.getMinApi(classDesc, methodDesc); 65 L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(), 66 classDesc, methodDesc, result); 67 if (result > 0) { 68 return result; 69 } 70 declaringClass = declaringClass.getSuperclass(); 71 } 72 return 1; 73 } 74 75 static class ApiChecker { 76 77 private Map<String, Integer> mFullLookup; 78 79 private Document mDoc; 80 81 private XPath mXPath; 82 ApiChecker(File apiFile)83 public ApiChecker(File apiFile) { 84 InputStream inputStream = null; 85 try { 86 if (apiFile == null || !apiFile.exists()) { 87 inputStream = getClass().getClassLoader().getResourceAsStream("api-versions.xml"); 88 } else { 89 inputStream = FileUtils.openInputStream(apiFile); 90 } 91 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 92 DocumentBuilder builder = factory.newDocumentBuilder(); 93 mDoc = builder.parse(inputStream); 94 XPathFactory xPathFactory = XPathFactory.newInstance(); 95 mXPath = xPathFactory.newXPath(); 96 buildFullLookup(); 97 } catch (Throwable t) { 98 L.e(t, "cannot load api descriptions from %s", apiFile); 99 } finally { 100 IOUtils.closeQuietly(inputStream); 101 } 102 } 103 buildFullLookup()104 private void buildFullLookup() throws XPathExpressionException { 105 NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes(); 106 mFullLookup = new HashMap<String, Integer>(allClasses.getLength() * 4); 107 for (int j = 0; j < allClasses.getLength(); j++) { 108 Node node = allClasses.item(j); 109 if (node.getNodeType() != Node.ELEMENT_NODE || !"class" 110 .equals(node.getNodeName())) { 111 continue; 112 } 113 //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue()); 114 int classSince = getSince(node); 115 String classDesc = node.getAttributes().getNamedItem("name").getNodeValue(); 116 117 final NodeList childNodes = node.getChildNodes(); 118 for (int i = 0; i < childNodes.getLength(); i++) { 119 Node child = childNodes.item(i); 120 if (child.getNodeType() != Node.ELEMENT_NODE || !"method" 121 .equals(child.getNodeName())) { 122 continue; 123 } 124 int methodSince = getSince(child); 125 int since = Math.max(classSince, methodSince); 126 String methodDesc = child.getAttributes().getNamedItem("name") 127 .getNodeValue(); 128 String key = cacheKey(classDesc, methodDesc); 129 mFullLookup.put(key, since); 130 } 131 } 132 } 133 134 /** 135 * Returns 0 if we cannot find the API level for the method. 136 */ getMinApi(String classDesc, String methodOrFieldDesc)137 public int getMinApi(String classDesc, String methodOrFieldDesc) { 138 if (mDoc == null || mXPath == null) { 139 return 1; 140 } 141 if (classDesc == null || classDesc.isEmpty()) { 142 return 1; 143 } 144 final String key = cacheKey(classDesc, methodOrFieldDesc); 145 Integer since = mFullLookup.get(key); 146 return since == null ? 0 : since; 147 } 148 cacheKey(String classDesc, String methodOrFieldDesc)149 private static String cacheKey(String classDesc, String methodOrFieldDesc) { 150 return classDesc + "~" + methodOrFieldDesc; 151 } 152 getSince(Node node)153 private static int getSince(Node node) { 154 final Node since = node.getAttributes().getNamedItem("since"); 155 if (since != null) { 156 final String nodeValue = since.getNodeValue(); 157 if (nodeValue != null && !nodeValue.isEmpty()) { 158 try { 159 return Integer.parseInt(nodeValue); 160 } catch (Throwable t) { 161 } 162 } 163 } 164 165 return 1; 166 } 167 } 168 } 169