/*
* 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 com.android.layoutlib.bridge.android;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.BridgeXmlPullAttributes;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* {@link BridgeXmlBlockParser} reimplements most of android.xml.XmlBlock.Parser.
* It delegates to both an instance of {@link XmlPullParser} and an instance of
* XmlPullAttributes (for the {@link AttributeSet} part).
*/
public class BridgeXmlBlockParser implements XmlResourceParser {
private final XmlPullParser mParser;
private final BridgeXmlPullAttributes mAttrib;
private final BridgeContext mContext;
private final boolean mPlatformFile;
private boolean mStarted = false;
private int mEventType = START_DOCUMENT;
private boolean mPopped = true; // default to true in case it's not pushed.
/**
* Builds a {@link BridgeXmlBlockParser}.
* @param parser The XmlPullParser to get the content from.
* @param context the Context.
* @param platformFile Indicates whether the the file is a platform file or not.
*/
public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
if (ParserFactory.LOG_PARSER) {
System.out.println("CRTE " + parser.toString());
}
mParser = parser;
mContext = context;
mPlatformFile = platformFile;
mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
if (mContext != null) {
mContext.pushParser(this);
mPopped = false;
}
}
public XmlPullParser getParser() {
return mParser;
}
public boolean isPlatformFile() {
return mPlatformFile;
}
public Object getViewCookie() {
if (mParser instanceof ILayoutPullParser) {
return ((ILayoutPullParser)mParser).getViewCookie();
}
return null;
}
public void ensurePopped() {
if (mContext != null && mPopped == false) {
mContext.popParser();
mPopped = true;
}
}
// ------- XmlResourceParser implementation
@Override
public void setFeature(String name, boolean state)
throws XmlPullParserException {
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
return;
}
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
return;
}
throw new XmlPullParserException("Unsupported feature: " + name);
}
@Override
public boolean getFeature(String name) {
if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
return true;
}
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
return true;
}
return false;
}
@Override
public void setProperty(String name, Object value) throws XmlPullParserException {
throw new XmlPullParserException("setProperty() not supported");
}
@Override
public Object getProperty(String name) {
return null;
}
@Override
public void setInput(Reader in) throws XmlPullParserException {
mParser.setInput(in);
}
@Override
public void setInput(InputStream inputStream, String inputEncoding)
throws XmlPullParserException {
mParser.setInput(inputStream, inputEncoding);
}
@Override
public void defineEntityReplacementText(String entityName,
String replacementText) throws XmlPullParserException {
throw new XmlPullParserException(
"defineEntityReplacementText() not supported");
}
@Override
public String getNamespacePrefix(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespacePrefix() not supported");
}
@Override
public String getInputEncoding() {
return null;
}
@Override
public String getNamespace(String prefix) {
throw new RuntimeException("getNamespace() not supported");
}
@Override
public int getNamespaceCount(int depth) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceCount() not supported");
}
@Override
public String getPositionDescription() {
return "Binary XML file line #" + getLineNumber();
}
@Override
public String getNamespaceUri(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceUri() not supported");
}
@Override
public int getColumnNumber() {
return -1;
}
@Override
public int getDepth() {
return mParser.getDepth();
}
@Override
public String getText() {
return mParser.getText();
}
@Override
public int getLineNumber() {
return mParser.getLineNumber();
}
@Override
public int getEventType() {
return mEventType;
}
@Override
public boolean isWhitespace() throws XmlPullParserException {
// Original comment: whitespace was stripped by aapt.
return mParser.isWhitespace();
}
@Override
public String getPrefix() {
throw new RuntimeException("getPrefix not supported");
}
@Override
public char[] getTextCharacters(int[] holderForStartAndLength) {
String txt = getText();
char[] chars = null;
if (txt != null) {
holderForStartAndLength[0] = 0;
holderForStartAndLength[1] = txt.length();
chars = new char[txt.length()];
txt.getChars(0, txt.length(), chars, 0);
}
return chars;
}
@Override
public String getNamespace() {
return mParser.getNamespace();
}
@Override
public String getName() {
return mParser.getName();
}
@Override
public String getAttributeNamespace(int index) {
return mParser.getAttributeNamespace(index);
}
@Override
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
@Override
public String getAttributePrefix(int index) {
throw new RuntimeException("getAttributePrefix not supported");
}
@Override
public boolean isEmptyElementTag() {
// XXX Need to detect this.
return false;
}
@Override
public int getAttributeCount() {
return mParser.getAttributeCount();
}
@Override
public String getAttributeValue(int index) {
return mParser.getAttributeValue(index);
}
@Override
public String getAttributeType(int index) {
return "CDATA";
}
@Override
public boolean isAttributeDefault(int index) {
return false;
}
@Override
public int nextToken() throws XmlPullParserException, IOException {
return next();
}
@Override
public String getAttributeValue(String namespace, String name) {
return mParser.getAttributeValue(namespace, name);
}
@Override
public int next() throws XmlPullParserException, IOException {
if (!mStarted) {
mStarted = true;
if (ParserFactory.LOG_PARSER) {
System.out.println("STRT " + mParser.toString());
}
return START_DOCUMENT;
}
int ev = mParser.next();
if (ParserFactory.LOG_PARSER) {
System.out.println("NEXT " + mParser.toString() + " " +
eventTypeToString(mEventType) + " -> " + eventTypeToString(ev));
}
if (ev == END_TAG && mParser.getDepth() == 1) {
// done with parser remove it from the context stack.
ensurePopped();
if (ParserFactory.LOG_PARSER) {
System.out.println("");
}
}
mEventType = ev;
return ev;
}
public static String eventTypeToString(int eventType) {
switch (eventType) {
case START_DOCUMENT:
return "START_DOC";
case END_DOCUMENT:
return "END_DOC";
case START_TAG:
return "START_TAG";
case END_TAG:
return "END_TAG";
case TEXT:
return "TEXT";
case CDSECT:
return "CDSECT";
case ENTITY_REF:
return "ENTITY_REF";
case IGNORABLE_WHITESPACE:
return "IGNORABLE_WHITESPACE";
case PROCESSING_INSTRUCTION:
return "PROCESSING_INSTRUCTION";
case COMMENT:
return "COMMENT";
case DOCDECL:
return "DOCDECL";
}
return "????";
}
@Override
public void require(int type, String namespace, String name)
throws XmlPullParserException {
if (type != getEventType()
|| (namespace != null && !namespace.equals(getNamespace()))
|| (name != null && !name.equals(getName())))
throw new XmlPullParserException("expected " + TYPES[type]
+ getPositionDescription());
}
@Override
public String nextText() throws XmlPullParserException, IOException {
if (getEventType() != START_TAG) {
throw new XmlPullParserException(getPositionDescription()
+ ": parser must be on START_TAG to read next text", this,
null);
}
int eventType = next();
if (eventType == TEXT) {
String result = getText();
eventType = next();
if (eventType != END_TAG) {
throw new XmlPullParserException(
getPositionDescription()
+ ": event TEXT it must be immediately followed by END_TAG",
this, null);
}
return result;
} else if (eventType == END_TAG) {
return "";
} else {
throw new XmlPullParserException(getPositionDescription()
+ ": parser must be on START_TAG or TEXT to read text",
this, null);
}
}
@Override
public int nextTag() throws XmlPullParserException, IOException {
int eventType = next();
if (eventType == TEXT && isWhitespace()) { // skip whitespace
eventType = next();
}
if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException(getPositionDescription()
+ ": expected start or end tag", this, null);
}
return eventType;
}
// AttributeSet implementation
@Override
public void close() {
// pass
}
@Override
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
return mAttrib.getAttributeBooleanValue(index, defaultValue);
}
@Override
public boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue) {
return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
}
@Override
public float getAttributeFloatValue(int index, float defaultValue) {
return mAttrib.getAttributeFloatValue(index, defaultValue);
}
@Override
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
}
@Override
public int getAttributeIntValue(int index, int defaultValue) {
return mAttrib.getAttributeIntValue(index, defaultValue);
}
@Override
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
}
@Override
public int getAttributeListValue(int index, String[] options, int defaultValue) {
return mAttrib.getAttributeListValue(index, options, defaultValue);
}
@Override
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
}
@Override
public int getAttributeNameResource(int index) {
return mAttrib.getAttributeNameResource(index);
}
@Override
public int getAttributeResourceValue(int index, int defaultValue) {
return mAttrib.getAttributeResourceValue(index, defaultValue);
}
@Override
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
}
@Override
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
}
@Override
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
}
@Override
public String getClassAttribute() {
return mAttrib.getClassAttribute();
}
@Override
public String getIdAttribute() {
return mAttrib.getIdAttribute();
}
@Override
public int getIdAttributeResourceValue(int defaultValue) {
return mAttrib.getIdAttributeResourceValue(defaultValue);
}
@Override
public int getStyleAttribute() {
return mAttrib.getStyleAttribute();
}
}