| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.apkcheck; |
| |
| import org.xml.sax.*; |
| import org.xml.sax.helpers.*; |
| import java.io.*; |
| |
| |
| /** |
| * Provides implementation for SAX parser. |
| */ |
| class ApiDescrHandler extends DefaultHandler { |
| /* |
| * Uber-container. |
| */ |
| private ApiList mApiList; |
| |
| /* |
| * Temporary objects, used as containers while we accumulate the |
| * innards. |
| */ |
| private PackageInfo mCurrentPackage = null; |
| private ClassInfo mCurrentClass = null; |
| private MethodInfo mCurrentMethod = null; |
| |
| /** |
| * Constructs an ApiDescrHandler. |
| * |
| * @param fileName Source file name, used for debugging. |
| */ |
| public ApiDescrHandler(ApiList apiList) { |
| mApiList = apiList; |
| } |
| |
| /** |
| * Returns the ApiList in its current state. Generally only |
| * makes sense to call here after parsing is completed. |
| */ |
| public ApiList getApiList() { |
| return mApiList; |
| } |
| |
| /** |
| * Processes start tags. If the file is malformed we will likely |
| * NPE, but this is captured by the caller. |
| * |
| * We currently assume that packages and classes only appear once, |
| * so all classes associated with a package are wrapped in a singular |
| * instance of <package>. We may want to remove this assumption |
| * by attempting to find an existing package/class with the same name. |
| */ |
| @Override |
| public void startElement(String uri, String localName, String qName, |
| Attributes attributes) { |
| |
| if (qName.equals("package")) { |
| /* top-most element */ |
| mCurrentPackage = mApiList.getOrCreatePackage( |
| attributes.getValue("name")); |
| } else if (qName.equals("class") || qName.equals("interface")) { |
| /* get class, gather fields/methods and interfaces */ |
| mCurrentClass = mCurrentPackage.getOrCreateClass( |
| attributes.getValue("name"), |
| attributes.getValue("extends"), |
| attributes.getValue("static")); |
| } else if (qName.equals("implements")) { |
| /* add name of interface to current class */ |
| mCurrentClass.addInterface(attributes.getValue("name")); |
| } else if (qName.equals("method")) { |
| /* hold object while we gather parameters */ |
| mCurrentMethod = new MethodInfo(attributes.getValue("name"), |
| attributes.getValue("return")); |
| } else if (qName.equals("constructor")) { |
| /* like "method", but has no name or return type */ |
| mCurrentMethod = new MethodInfo("<init>", "void"); |
| |
| /* |
| * If this is a non-static inner class, we want to add the |
| * "hidden" outer class parameter as the first parameter. |
| * We can tell if it's an inner class because the class name |
| * will include a '$' (it has been normalized already). |
| */ |
| String staticClass = mCurrentClass.getStatic(); |
| if (staticClass == null) { |
| /* |
| * We're parsing an APK file, which means we can't know |
| * if the class we're referencing is static or not. We |
| * also already have the "secret" first parameter |
| * represented in the method parameter list, so we don't |
| * need to insert it here. |
| */ |
| } else if ("false".equals(staticClass)) { |
| String className = mCurrentClass.getName(); |
| int dollarIndex = className.indexOf('$'); |
| if (dollarIndex >= 0) { |
| String outerClass = className.substring(0, dollarIndex); |
| //System.out.println("--- inserting " + |
| // mCurrentPackage.getName() + "." + outerClass + |
| // " into constructor for " + className); |
| mCurrentMethod.addParameter(mCurrentPackage.getName() + |
| "." + outerClass); |
| } |
| } |
| } else if (qName.equals("field")) { |
| /* add to current class */ |
| FieldInfo fInfo = new FieldInfo(attributes.getValue("name"), |
| attributes.getValue("type")); |
| mCurrentClass.addField(fInfo); |
| } else if (qName.equals("parameter")) { |
| /* add to current method */ |
| mCurrentMethod.addParameter(attributes.getValue("type")); |
| } |
| } |
| |
| /** |
| * Processes end tags. Generally these add the under-construction |
| * item to the appropriate container. |
| */ |
| @Override |
| public void endElement(String uri, String localName, String qName) { |
| if (qName.equals("method") || qName.equals("constructor")) { |
| /* add method to class */ |
| mCurrentClass.addMethod(mCurrentMethod); |
| mCurrentMethod = null; |
| } else if (qName.equals("class") || qName.equals("interface")) { |
| mCurrentClass = null; |
| } else if (qName.equals("package")) { |
| mCurrentPackage = null; |
| } |
| } |
| } |
| |