• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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