Home | History | Annotate | Download | only in apkcheck
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.apkcheck;
     18 
     19 import org.xml.sax.*;
     20 import org.xml.sax.helpers.*;
     21 import java.io.*;
     22 
     23 
     24 /**
     25  * Provides implementation for SAX parser.
     26  */
     27 class ApiDescrHandler extends DefaultHandler {
     28     /*
     29      * Uber-container.
     30      */
     31     private ApiList mApiList;
     32 
     33     /*
     34      * Temporary objects, used as containers while we accumulate the
     35      * innards.
     36      */
     37     private PackageInfo mCurrentPackage = null;
     38     private ClassInfo mCurrentClass = null;
     39     private MethodInfo mCurrentMethod = null;
     40 
     41     /**
     42      * Constructs an ApiDescrHandler.
     43      *
     44      * @param fileName Source file name, used for debugging.
     45      */
     46     public ApiDescrHandler(ApiList apiList) {
     47         mApiList = apiList;
     48     }
     49 
     50     /**
     51      * Returns the ApiList in its current state.  Generally only
     52      * makes sense to call here after parsing is completed.
     53      */
     54     public ApiList getApiList() {
     55         return mApiList;
     56     }
     57 
     58     /**
     59      * Processes start tags.  If the file is malformed we will likely
     60      * NPE, but this is captured by the caller.
     61      *
     62      * We currently assume that packages and classes only appear once,
     63      * so all classes associated with a package are wrapped in a singular
     64      * instance of <package>.  We may want to remove this assumption
     65      * by attempting to find an existing package/class with the same name.
     66      */
     67     @Override
     68     public void startElement(String uri, String localName, String qName,
     69             Attributes attributes) {
     70 
     71         if (qName.equals("package")) {
     72             /* top-most element */
     73             mCurrentPackage = mApiList.getOrCreatePackage(
     74                     attributes.getValue("name"));
     75         } else if (qName.equals("class") || qName.equals("interface")) {
     76             /* get class, gather fields/methods and interfaces */
     77             mCurrentClass = mCurrentPackage.getOrCreateClass(
     78                     attributes.getValue("name"),
     79                     attributes.getValue("extends"),
     80                     attributes.getValue("static"));
     81         } else if (qName.equals("implements")) {
     82             /* add name of interface to current class */
     83             mCurrentClass.addInterface(attributes.getValue("name"));
     84         } else if (qName.equals("method")) {
     85             /* hold object while we gather parameters */
     86             mCurrentMethod = new MethodInfo(attributes.getValue("name"),
     87                 attributes.getValue("return"));
     88         } else if (qName.equals("constructor")) {
     89             /* like "method", but has no name or return type */
     90             mCurrentMethod = new MethodInfo("<init>", "void");
     91 
     92             /*
     93              * If this is a non-static inner class, we want to add the
     94              * "hidden" outer class parameter as the first parameter.
     95              * We can tell if it's an inner class because the class name
     96              * will include a '$' (it has been normalized already).
     97              */
     98             String staticClass = mCurrentClass.getStatic();
     99             if (staticClass == null) {
    100                 /*
    101                  * We're parsing an APK file, which means we can't know
    102                  * if the class we're referencing is static or not.  We
    103                  * also already have the "secret" first parameter
    104                  * represented in the method parameter list, so we don't
    105                  * need to insert it here.
    106                  */
    107             } else if ("false".equals(staticClass)) {
    108                 String className = mCurrentClass.getName();
    109                 int dollarIndex = className.indexOf('$');
    110                 if (dollarIndex >= 0) {
    111                     String outerClass = className.substring(0, dollarIndex);
    112                     //System.out.println("--- inserting " +
    113                     //    mCurrentPackage.getName() + "." + outerClass +
    114                     //    " into constructor for " + className);
    115                     mCurrentMethod.addParameter(mCurrentPackage.getName() +
    116                         "." + outerClass);
    117                 }
    118             }
    119         } else if (qName.equals("field")) {
    120             /* add to current class */
    121             FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
    122                     attributes.getValue("type"));
    123             mCurrentClass.addField(fInfo);
    124         } else if (qName.equals("parameter")) {
    125             /* add to current method */
    126             mCurrentMethod.addParameter(attributes.getValue("type"));
    127         }
    128     }
    129 
    130     /**
    131      * Processes end tags.  Generally these add the under-construction
    132      * item to the appropriate container.
    133      */
    134     @Override
    135     public void endElement(String uri, String localName, String qName) {
    136         if (qName.equals("method") || qName.equals("constructor")) {
    137             /* add method to class */
    138             mCurrentClass.addMethod(mCurrentMethod);
    139             mCurrentMethod = null;
    140         } else if (qName.equals("class") || qName.equals("interface")) {
    141             mCurrentClass = null;
    142         } else if (qName.equals("package")) {
    143             mCurrentPackage = null;
    144         }
    145     }
    146 }
    147 
    148