SdkUtil.java revision fead9ca09b117136b35bc5bf137340a754f9eddd
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
14package android.databinding.tool.reflection;
15
16import com.google.common.base.Preconditions;
17
18import org.w3c.dom.Document;
19import org.w3c.dom.Node;
20import org.w3c.dom.NodeList;
21
22import android.databinding.tool.util.L;
23
24import java.io.File;
25import java.util.HashMap;
26import java.util.Map;
27
28import javax.xml.parsers.DocumentBuilder;
29import javax.xml.parsers.DocumentBuilderFactory;
30import javax.xml.xpath.XPath;
31import javax.xml.xpath.XPathExpressionException;
32import javax.xml.xpath.XPathFactory;
33
34/**
35 * Class that is used for SDK related stuff.
36 * <p>
37 * Must be initialized with the sdk location to work properly
38 */
39public class SdkUtil {
40
41    static File mSdkPath;
42
43    static ApiChecker mApiChecker;
44
45    static int mMinSdk;
46
47    public static void initialize(int minSdk, File sdkPath) {
48        mSdkPath = sdkPath;
49        mMinSdk = minSdk;
50        mApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath()
51                + "/platform-tools/api/api-versions.xml"));
52        L.d("SdkUtil init, minSdk: %s", minSdk);
53    }
54
55    public static int getMinApi(ModelClass modelClass) {
56        return mApiChecker.getMinApi(modelClass.getJniDescription(), null);
57    }
58
59    public static int getMinApi(ModelMethod modelMethod) {
60        ModelClass declaringClass = modelMethod.getDeclaringClass();
61        Preconditions.checkNotNull(mApiChecker, "should've initialized api checker");
62        while (declaringClass != null) {
63            String classDesc = declaringClass.getJniDescription();
64            String methodDesc = modelMethod.getJniDescription();
65            int result = mApiChecker.getMinApi(classDesc, methodDesc);
66            L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(),
67                    classDesc, methodDesc, result);
68            if (result > 1) {
69                return result;
70            }
71            declaringClass = declaringClass.getSuperclass();
72        }
73        return 1;
74    }
75
76    private static class ApiChecker {
77
78        private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
79
80        private Document mDoc;
81
82        private XPath mXPath;
83
84        public ApiChecker(File apiFile) {
85            try {
86                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
87                DocumentBuilder builder = factory.newDocumentBuilder();
88                mDoc = builder.parse(apiFile);
89                XPathFactory xPathFactory = XPathFactory.newInstance();
90                mXPath = xPathFactory.newXPath();
91                buildFullLookup();
92            } catch (Throwable t) {
93                L.e(t, "cannot load api descriptions from %s", apiFile);
94            }
95        }
96
97        private void buildFullLookup() throws XPathExpressionException {
98            NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes();
99            for (int j = 0; j < allClasses.getLength(); j++) {
100                Node node = allClasses.item(j);
101                if (node.getNodeType() != Node.ELEMENT_NODE || !"class"
102                        .equals(node.getNodeName())) {
103                    continue;
104                }
105                //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue());
106                int classSince = getSince(node);
107                String classDesc = node.getAttributes().getNamedItem("name").getNodeValue();
108
109                final NodeList childNodes = node.getChildNodes();
110                for (int i = 0; i < childNodes.getLength(); i++) {
111                    Node child = childNodes.item(i);
112                    if (child.getNodeType() != Node.ELEMENT_NODE || !"method"
113                            .equals(child.getNodeName())) {
114                        continue;
115                    }
116                    int methodSince = getSince(child);
117                    int since = Math.max(classSince, methodSince);
118                    if (since > SdkUtil.mMinSdk) {
119                        String methodDesc = child.getAttributes().getNamedItem("name")
120                                .getNodeValue();
121                        String key = cacheKey(classDesc, methodDesc);
122                        mFullLookup.put(key, since);
123                    }
124                }
125            }
126        }
127
128        public int getMinApi(String classDesc, String methodOrFieldDesc) {
129            if (mDoc == null || mXPath == null) {
130                return 1;
131            }
132            if (classDesc == null || classDesc.isEmpty()) {
133                return 1;
134            }
135            final String key = cacheKey(classDesc, methodOrFieldDesc);
136            Integer since = mFullLookup.get(key);
137            return since == null ? 1 : since;
138        }
139
140        private static String cacheKey(String classDesc, String methodOrFieldDesc) {
141            return classDesc + "~" + methodOrFieldDesc;
142        }
143
144        private static int getSince(Node node) {
145            final Node since = node.getAttributes().getNamedItem("since");
146            if (since != null) {
147                final String nodeValue = since.getNodeValue();
148                if (nodeValue != null && !nodeValue.isEmpty()) {
149                    try {
150                        return Integer.parseInt(nodeValue);
151                    } catch (Throwable t) {
152                    }
153                }
154            }
155
156            return 1;
157        }
158    }
159}
160