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 */ ApiDescrHandler(ApiList apiList)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 */ getApiList()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 startElement(String uri, String localName, String qName, Attributes attributes)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 endElement(String uri, String localName, String qName)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