Home | History | Annotate | Download | only in reflection
      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 
     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 
     54     public static int getMinApi(ModelClass modelClass) {
     55         return sApiChecker.getMinApi(modelClass.getJniDescription(), null);
     56     }
     57 
     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 
     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 
    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          */
    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 
    149         private static String cacheKey(String classDesc, String methodOrFieldDesc) {
    150             return classDesc + "~" + methodOrFieldDesc;
    151         }
    152 
    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