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