/* * 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; } } }