blob: 80011308f4736dd81d97b0982e122d60f9d21eef [file] [log] [blame]
/*
* Copyright (C) 2008 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 android.tests.sigtest;
import android.tests.sigtest.JDiffClassDescription.JDiffConstructor;
import android.tests.sigtest.JDiffClassDescription.JDiffField;
import android.tests.sigtest.JDiffClassDescription.JDiffMethod;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
/**
* Entry class for signature test.
*/
public class SignatureTest {
private static final String TAG_ROOT = "api";
private static final String TAG_PACKAGE = "package";
private static final String TAG_CLASS = "class";
private static final String TAG_INTERFACE = "interface";
private static final String TAG_IMPLEMENTS = "implements";
private static final String TAG_CONSTRUCTOR = "constructor";
private static final String TAG_METHOD = "method";
private static final String TAG_PARAM = "parameter";
private static final String TAG_EXCEPTION = "exception";
private static final String TAG_FIELD = "field";
private static final String MODIFIER_ABSTRACT = "abstract";
private static final String MODIFIER_FINAL = "final";
private static final String MODIFIER_NATIVE = "native";
private static final String MODIFIER_PRIVATE = "private";
private static final String MODIFIER_PROTECTED = "protected";
private static final String MODIFIER_PUBLIC = "public";
private static final String MODIFIER_STATIC = "static";
private static final String MODIFIER_SYNCHRONIZED = "synchronized";
private static final String MODIFIER_TRANSIENT = "transient";
private static final String MODIFIER_VOLATILE = "volatile";
private static final String MODIFIER_VISIBILITY = "visibility";
private static final String ATTRIBUTE_NAME = "name";
private static final String ATTRIBUTE_EXTENDS = "extends";
private static final String ATTRIBUTE_TYPE = "type";
private static final String ATTRIBUTE_RETURN = "return";
private static ArrayList<String> mDebugArray = new ArrayList<String>();
private HashSet<String> mKeyTagSet;
private ArrayList<ResultObserver> mReportObserverList;
private ResultObserver resultObserver;
public SignatureTest(ResultObserver resultObserver) {
this.resultObserver = resultObserver;
mReportObserverList = new ArrayList<ResultObserver>();
mKeyTagSet = new HashSet<String>();
mKeyTagSet.addAll(Arrays.asList(new String[] {
TAG_PACKAGE, TAG_CLASS, TAG_INTERFACE, TAG_IMPLEMENTS, TAG_CONSTRUCTOR,
TAG_METHOD, TAG_PARAM, TAG_EXCEPTION, TAG_FIELD }));
}
public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
{
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) { }
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals(firstElementName)) {
throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
", expected " + firstElementName);
}
}
/**
* Signature test entry point.
*/
public void start(XmlPullParser parser) throws XmlPullParserException, IOException {
JDiffClassDescription currentClass = null;
String currentPackage = "";
JDiffMethod currentMethod = null;
SignatureTest.beginDocument(parser, TAG_ROOT);
int type;
while (true) {
type = XmlPullParser.START_DOCUMENT;
while ((type=parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.END_TAG) {
}
if (type == XmlPullParser.END_TAG) {
if (TAG_CLASS.equals(parser.getName())
|| TAG_INTERFACE.equals(parser.getName())) {
currentClass.checkSignatureCompliance();
} else if (TAG_PACKAGE.equals(parser.getName())) {
currentPackage = "";
}
continue;
}
if (type == XmlPullParser.END_DOCUMENT) {
break;
}
String tagname = parser.getName();
if (!mKeyTagSet.contains(tagname)) {
continue;
}
if (type == XmlPullParser.START_TAG && tagname.equals(TAG_PACKAGE)) {
currentPackage = parser.getAttributeValue(null, ATTRIBUTE_NAME);
} else if (tagname.equals(TAG_CLASS)) {
currentClass = loadClassInfo(parser, false, currentPackage);
} else if (tagname.equals(TAG_INTERFACE)) {
currentClass = loadClassInfo(parser, true, currentPackage);
} else if (tagname.equals(TAG_IMPLEMENTS)) {
currentClass.addImplInterface(parser.getAttributeValue(null, ATTRIBUTE_NAME));
} else if (tagname.equals(TAG_CONSTRUCTOR)) {
JDiffConstructor constructor = loadConstructorInfo(parser, currentClass);
currentClass.addConstructor(constructor);
currentMethod = constructor;
} else if (tagname.equals(TAG_METHOD)) {
currentMethod = loadMethodInfo(currentClass.getClassName(), parser);
currentClass.addMethod(currentMethod);
} else if (tagname.equals(TAG_PARAM)) {
currentMethod.addParam(parser.getAttributeValue(null, ATTRIBUTE_TYPE));
} else if (tagname.equals(TAG_EXCEPTION)) {
currentMethod.addException(parser.getAttributeValue(null, ATTRIBUTE_TYPE));
} else if (tagname.equals(TAG_FIELD)) {
JDiffField field = loadFieldInfo(currentClass.getClassName(), parser);
currentClass.addField(field);
} else {
throw new RuntimeException(
"unknow tag exception:" + tagname);
}
}
}
public static void log(final String msg) {
mDebugArray.add(msg);
}
public void addReportObserver(ResultObserver observer) {
mReportObserverList.add(observer);
}
public void removeReportObserver(ResultObserver observer) {
mReportObserverList.remove(observer);
}
public void clearReportObserverList() {
mReportObserverList.clear();
}
/**
* Load field information from xml to memory.
*
* @param className of the class being examined which will be shown in error messages
* @param parser The XmlPullParser which carries the xml information.
* @return the new field
*/
private JDiffField loadFieldInfo(String className, XmlPullParser parser) {
String fieldName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
String fieldType = parser.getAttributeValue(null, ATTRIBUTE_TYPE);
int modifier = jdiffModifierToReflectionFormat(className, parser);
return new JDiffField(fieldName, fieldType, modifier);
}
/**
* Load method information from xml to memory.
*
* @param className of the class being examined which will be shown in error messages
* @param parser The XmlPullParser which carries the xml information.
* @return the newly loaded method.
*/
private JDiffMethod loadMethodInfo(String className, XmlPullParser parser) {
String methodName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
String returnType = parser.getAttributeValue(null, ATTRIBUTE_RETURN);
int modifier = jdiffModifierToReflectionFormat(className, parser);
return new JDiffMethod(methodName, modifier, returnType);
}
/**
* Load constructor information from xml to memory.
*
* @param parser The XmlPullParser which carries the xml information.
* @param currentClass the current class being loaded.
* @return the new constructor
*/
private JDiffConstructor loadConstructorInfo(XmlPullParser parser,
JDiffClassDescription currentClass) {
String name = currentClass.getClassName();
int modifier = jdiffModifierToReflectionFormat(name, parser);
return new JDiffConstructor(name, modifier);
}
/**
* Load class or interface information to memory.
*
* @param parser The XmlPullParser which carries the xml information.
* @param isInterface true if the current class is an interface, otherwise is false.
* @param pkg the name of the java package this class can be found in.
* @return the new class description.
*/
private JDiffClassDescription loadClassInfo(XmlPullParser parser,
boolean isInterface,
String pkg) {
String className = parser.getAttributeValue(null, ATTRIBUTE_NAME);
JDiffClassDescription currentClass = new JDiffClassDescription(pkg,
className,
resultObserver);
currentClass.setModifier(jdiffModifierToReflectionFormat(className, parser));
currentClass.setType(isInterface ? JDiffClassDescription.JDiffType.INTERFACE :
JDiffClassDescription.JDiffType.CLASS);
currentClass.setExtendsClass(parser.getAttributeValue(null, ATTRIBUTE_EXTENDS));
return currentClass;
}
/**
* Convert string modifier to int modifier.
*
* @param name of the class/method/field being examined which will be shown in error messages
* @param key modifier name
* @param value modifier value
* @return converted modifier value
*/
private static int modifierDescriptionToReflectedType(String name, String key, String value) {
if (key.equals(MODIFIER_ABSTRACT)) {
return value.equals("true") ? Modifier.ABSTRACT : 0;
} else if (key.equals(MODIFIER_FINAL)) {
return value.equals("true") ? Modifier.FINAL : 0;
} else if (key.equals(MODIFIER_NATIVE)) {
return value.equals("true") ? Modifier.NATIVE : 0;
} else if (key.equals(MODIFIER_STATIC)) {
return value.equals("true") ? Modifier.STATIC : 0;
} else if (key.equals(MODIFIER_SYNCHRONIZED)) {
return value.equals("true") ? Modifier.SYNCHRONIZED : 0;
} else if (key.equals(MODIFIER_TRANSIENT)) {
return value.equals("true") ? Modifier.TRANSIENT : 0;
} else if (key.equals(MODIFIER_VOLATILE)) {
return value.equals("true") ? Modifier.VOLATILE : 0;
} else if (key.equals(MODIFIER_VISIBILITY)) {
if (value.equals(MODIFIER_PRIVATE)) {
throw new RuntimeException("Private visibility found in API spec: " + name);
} else if (value.equals(MODIFIER_PROTECTED)) {
return Modifier.PROTECTED;
} else if (value.equals(MODIFIER_PUBLIC)) {
return Modifier.PUBLIC;
} else if ("".equals(value)) {
// If the visibility is "", it means it has no modifier.
// which is package private. We should return 0 for this modifier.
return 0;
} else {
throw new RuntimeException("Unknown modifier found in API spec: " + value);
}
}
return 0;
}
/**
* Transfer string modifier to int one.
*
* @param name of the class/method/field being examined which will be shown in error messages
* @param parser XML resource parser
* @return converted modifier
*/
private static int jdiffModifierToReflectionFormat(String name, XmlPullParser parser){
int modifier = 0;
for (int i = 0;i < parser.getAttributeCount();i++) {
modifier |= modifierDescriptionToReflectedType(name, parser.getAttributeName(i),
parser.getAttributeValue(i));
}
return modifier;
}
}