SdkUtil.java revision fead9ca09b117136b35bc5bf137340a754f9eddd
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
1697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport com.google.common.base.Preconditions;
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount
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;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount
2497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.io.File;
2597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.HashMap;
2697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport java.util.Map;
2797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
2897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.parsers.DocumentBuilder;
2997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.parsers.DocumentBuilderFactory;
3097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPath;
3197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPathExpressionException;
3297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarimport javax.xml.xpath.XPathFactory;
3397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
3497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar/**
3597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Class that is used for SDK related stuff.
3697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * <p>
3797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar * Must be initialized with the sdk location to work properly
3897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar */
3997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyarpublic class SdkUtil {
4097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
4197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    static File mSdkPath;
4297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
4397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    static ApiChecker mApiChecker;
4497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
4597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    static int mMinSdk;
4697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
4797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static void initialize(int minSdk, File sdkPath) {
4897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mSdkPath = sdkPath;
4997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mMinSdk = minSdk;
5097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        mApiChecker = new ApiChecker(new File(sdkPath.getAbsolutePath()
5197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                + "/platform-tools/api/api-versions.xml"));
5297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        L.d("SdkUtil init, minSdk: %s", minSdk);
5397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
5497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
5597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static int getMinApi(ModelClass modelClass) {
5697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return mApiChecker.getMinApi(modelClass.getJniDescription(), null);
5797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
5897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
5997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    public static int getMinApi(ModelMethod modelMethod) {
6097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        ModelClass declaringClass = modelMethod.getDeclaringClass();
6197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        Preconditions.checkNotNull(mApiChecker, "should've initialized api checker");
6297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        while (declaringClass != null) {
6397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            String classDesc = declaringClass.getJniDescription();
6497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            String methodDesc = modelMethod.getJniDescription();
6597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            int result = mApiChecker.getMinApi(classDesc, methodDesc);
6697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            L.d("checking method api for %s, class:%s method:%s. result: %d", modelMethod.getName(),
6797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    classDesc, methodDesc, result);
6897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            if (result > 1) {
6997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                return result;
7097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
7197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            declaringClass = declaringClass.getSuperclass();
7297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
7397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        return 1;
7497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
7597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
7697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    private static class ApiChecker {
7797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
78895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar        private Map<String, Integer> mFullLookup = new HashMap<String, Integer>();
7997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
8097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private Document mDoc;
8197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
8297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private XPath mXPath;
8397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
8497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public ApiChecker(File apiFile) {
8597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            try {
8697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
8797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                DocumentBuilder builder = factory.newDocumentBuilder();
8897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                mDoc = builder.parse(apiFile);
8997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                XPathFactory xPathFactory = XPathFactory.newInstance();
9097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                mXPath = xPathFactory.newXPath();
9197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                buildFullLookup();
9297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            } catch (Throwable t) {
9397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                L.e(t, "cannot load api descriptions from %s", apiFile);
9497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
9597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
9697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
9797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private void buildFullLookup() throws XPathExpressionException {
9897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            NodeList allClasses = mDoc.getChildNodes().item(0).getChildNodes();
9997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            for (int j = 0; j < allClasses.getLength(); j++) {
10097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                Node node = allClasses.item(j);
10197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                if (node.getNodeType() != Node.ELEMENT_NODE || !"class"
10297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        .equals(node.getNodeName())) {
10397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    continue;
10497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                }
10597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                //L.d("checking node %s", node.getAttributes().getNamedItem("name").getNodeValue());
10697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                int classSince = getSince(node);
10797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                String classDesc = node.getAttributes().getNamedItem("name").getNodeValue();
10897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
10997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                final NodeList childNodes = node.getChildNodes();
11097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                for (int i = 0; i < childNodes.getLength(); i++) {
11197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    Node child = childNodes.item(i);
11297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    if (child.getNodeType() != Node.ELEMENT_NODE || !"method"
11397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                            .equals(child.getNodeName())) {
11497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        continue;
11597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    }
11697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    int methodSince = getSince(child);
11797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    int since = Math.max(classSince, methodSince);
11897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    if (since > SdkUtil.mMinSdk) {
11997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        String methodDesc = child.getAttributes().getNamedItem("name")
12097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                                .getNodeValue();
12197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        String key = cacheKey(classDesc, methodDesc);
12297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        mFullLookup.put(key, since);
12397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    }
12497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                }
12597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
12697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
12797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
12897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        public int getMinApi(String classDesc, String methodOrFieldDesc) {
12997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            if (mDoc == null || mXPath == null) {
13097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                return 1;
13197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
13297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            if (classDesc == null || classDesc.isEmpty()) {
13397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                return 1;
13497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
13597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            final String key = cacheKey(classDesc, methodOrFieldDesc);
13697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            Integer since = mFullLookup.get(key);
13797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return since == null ? 1 : since;
13897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
13997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
14097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private static String cacheKey(String classDesc, String methodOrFieldDesc) {
14197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return classDesc + "~" + methodOrFieldDesc;
14297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
14397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
14497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        private static int getSince(Node node) {
14597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            final Node since = node.getAttributes().getNamedItem("since");
14697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            if (since != null) {
14797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                final String nodeValue = since.getNodeValue();
14897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                if (nodeValue != null && !nodeValue.isEmpty()) {
14997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    try {
15097d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                        return Integer.parseInt(nodeValue);
15197d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    } catch (Throwable t) {
15297d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                    }
15397d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar                }
15497d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            }
15597d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar
15697d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar            return 1;
15797d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar        }
15897d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar    }
15997d6ddf47f4ff1abb3ed5201ce5232163f5325b1Yigit Boyar}
160