blob: 18afa98046a6ff837c00aaf3914b3d70d575dd5e [file] [log] [blame]
/*
* Copyright (C) 2017 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.signature.cts;
import static android.signature.cts.CurrentApi.ATTRIBUTE_NAME;
import static android.signature.cts.CurrentApi.ATTRIBUTE_TYPE;
import static android.signature.cts.CurrentApi.TAG_CLASS;
import static android.signature.cts.CurrentApi.TAG_CONSTRUCTOR;
import static android.signature.cts.CurrentApi.TAG_EXCEPTION;
import static android.signature.cts.CurrentApi.TAG_FIELD;
import static android.signature.cts.CurrentApi.TAG_IMPLEMENTS;
import static android.signature.cts.CurrentApi.TAG_INTERFACE;
import static android.signature.cts.CurrentApi.TAG_METHOD;
import static android.signature.cts.CurrentApi.TAG_PACKAGE;
import static android.signature.cts.CurrentApi.TAG_PARAM;
import static android.signature.cts.CurrentApi.TAG_ROOT;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
/**
* Parses an XML api definition file and constructs and populates an {@link JDiffClassDescription}
* for every class.
*
* <p>The definition file is converted into a {@link Stream} of {@link JDiffClassDescription}.
*/
public class ApiDocumentParser {
private static final Set<String> KEY_TAG_SET;
static {
KEY_TAG_SET = new HashSet<>();
Collections.addAll(KEY_TAG_SET,
TAG_PACKAGE,
TAG_CLASS,
TAG_INTERFACE,
TAG_IMPLEMENTS,
TAG_CONSTRUCTOR,
TAG_METHOD,
TAG_PARAM,
TAG_EXCEPTION,
TAG_FIELD);
}
private final String tag;
private final XmlPullParserFactory factory;
public ApiDocumentParser(String tag) throws XmlPullParserException {
this.tag = tag;
factory = XmlPullParserFactory.newInstance();
}
public Stream<JDiffClassDescription> parseAsStream(InputStream inputStream)
throws XmlPullParserException, IOException {
XmlPullParser parser = factory.newPullParser();
parser.setInput(inputStream, null);
return StreamSupport.stream(new ClassDescriptionSpliterator(parser), false);
}
private class ClassDescriptionSpliterator implements Spliterator<JDiffClassDescription> {
private final XmlPullParser parser;
JDiffClassDescription currentClass = null;
String currentPackage = "";
JDiffClassDescription.JDiffMethod currentMethod = null;
ClassDescriptionSpliterator(XmlPullParser parser) throws IOException, XmlPullParserException {
this.parser = parser;
logd(String.format("Name: %s", parser.getName()));
logd(String.format("Text: %s", parser.getText()));
logd(String.format("Namespace: %s", parser.getNamespace()));
logd(String.format("Line Number: %s", parser.getLineNumber()));
logd(String.format("Column Number: %s", parser.getColumnNumber()));
logd(String.format("Position Description: %s", parser.getPositionDescription()));
beginDocument(parser, TAG_ROOT);
}
@Override
public boolean tryAdvance(Consumer<? super JDiffClassDescription> action) {
JDiffClassDescription classDescription;
try {
classDescription = next();
} catch (IOException|XmlPullParserException e) {
throw new RuntimeException(e);
}
if (classDescription == null) {
return false;
}
action.accept(classDescription);
return true;
}
@Override
public Spliterator<JDiffClassDescription> trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return ORDERED | DISTINCT | NONNULL | IMMUTABLE;
}
private void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
do {
type = parser.next();
} while (type != 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);
}
}
private JDiffClassDescription next() throws IOException, XmlPullParserException {
int type;
while (true) {
do {
type = parser.next();
} while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.END_TAG);
if (type == XmlPullParser.END_DOCUMENT) {
logd("Reached end of document");
break;
}
String tagname = parser.getName();
if (type == XmlPullParser.END_TAG) {
if (TAG_CLASS.equals(tagname) || TAG_INTERFACE.equals(tagname)) {
logd("Reached end of class: " + currentClass);
return currentClass;
} else if (TAG_PACKAGE.equals(tagname)) {
currentPackage = "";
}
continue;
}
if (!KEY_TAG_SET.contains(tagname)) {
continue;
}
if (tagname.equals(TAG_PACKAGE)) {
currentPackage = parser.getAttributeValue(null, ATTRIBUTE_NAME);
} else if (tagname.equals(TAG_CLASS)) {
currentClass = CurrentApi.loadClassInfo(
parser, false, currentPackage);
} else if (tagname.equals(TAG_INTERFACE)) {
currentClass = CurrentApi.loadClassInfo(
parser, true, currentPackage);
} else if (tagname.equals(TAG_IMPLEMENTS)) {
currentClass.addImplInterface(parser.getAttributeValue(null, ATTRIBUTE_NAME));
} else if (tagname.equals(TAG_CONSTRUCTOR)) {
JDiffClassDescription.JDiffConstructor constructor =
CurrentApi.loadConstructorInfo(parser, currentClass);
currentClass.addConstructor(constructor);
currentMethod = constructor;
} else if (tagname.equals(TAG_METHOD)) {
currentMethod = CurrentApi.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)) {
JDiffClassDescription.JDiffField field = CurrentApi.loadFieldInfo(currentClass.getClassName(), parser);
currentClass.addField(field);
} else {
throw new RuntimeException(
"unknown tag exception:" + tagname);
}
if (currentPackage != null) {
logd(String.format("currentPackage: %s", currentPackage));
}
if (currentClass != null) {
logd(String.format("currentClass: %s", currentClass.toSignatureString()));
}
if (currentMethod != null) {
logd(String.format("currentMethod: %s", currentMethod.toSignatureString()));
}
}
return null;
}
}
private void logd(String msg) {
Log.d(tag, msg);
}
}