diff options
Diffstat (limited to 'src/com/google/wireless/gdata/parser/xml/SimplePullParser.java')
-rw-r--r-- | src/com/google/wireless/gdata/parser/xml/SimplePullParser.java | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/com/google/wireless/gdata/parser/xml/SimplePullParser.java b/src/com/google/wireless/gdata/parser/xml/SimplePullParser.java new file mode 100644 index 0000000..d16aa5f --- /dev/null +++ b/src/com/google/wireless/gdata/parser/xml/SimplePullParser.java @@ -0,0 +1,300 @@ +/* + * 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.google.wireless.gdata.parser.xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * This is an abstraction of a pull parser that provides several benefits:<ul> + * <li>it is easier to use robustly because it makes it trivial to handle unexpected tags (which + * might have children)</li> + * <li>it makes the handling of text (cdata) blocks more convenient</li> + * <li>it provides convenient methods for getting a mandatory attribute (and throwing an exception + * if it is missing) or an optional attribute (and using a default value if it is missing) + * </ul> + */ +public class SimplePullParser { + public static final String TEXT_TAG = "![CDATA["; + + private final XmlPullParser mParser; + private String mCurrentStartTag; + + /** + * Constructs a new SimplePullParser to parse the xml + * @param parser the underlying parser to use + */ + public SimplePullParser(XmlPullParser parser) { + mParser = parser; + mCurrentStartTag = null; + } + + /** + * Returns the tag of the next element whose depth is parentDepth plus one + * or null if there are no more such elements before the next start tag. When this returns, + * getDepth() and all methods relating to attributes will refer to the element whose tag is + * returned. + * + * @param parentDepth the depth of the parrent of the item to be returned + * @param textBuffer if null then text blocks will be ignored. If + * non-null then text blocks will be added to the builder and TEXT_TAG + * will be returned when one is found + * @return the next of the next child element's tag, TEXT_TAG if a text block is found, or null + * if there are no more child elements or DATA blocks + * @throws IOException propogated from the underlying parser + * @throws ParseException if there was an error parsing the xml. + */ + public String nextTagOrText(int parentDepth, StringBuffer textBuffer) + throws IOException, ParseException { + while (true) { + int eventType = 0; + try { + eventType = mParser.next(); + } catch (XmlPullParserException e) { + throw new ParseException(e); + } + int depth = mParser.getDepth(); + mCurrentStartTag = null; + + if (eventType == XmlPullParser.START_TAG && depth == parentDepth + 1) { + mCurrentStartTag = mParser.getName(); + // TODO: this is an example of how to do logging of the XML +// if (mLogTag != null && Log.isLoggable(mLogTag, Log.DEBUG)) { +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < depth; i++) sb.append(" "); +// sb.append("<").append(mParser.getName()); +// int count = mParser.getAttributeCount(); +// for (int i = 0; i < count; i++) { +// sb.append(" "); +// sb.append(mParser.getAttributeName(i)); +// sb.append("=\""); +// sb.append(mParser.getAttributeValue(i)); +// sb.append("\""); +// } +// sb.append(">"); +// Log.d(mLogTag, sb.toString()); +// } + return mParser.getName(); + } + + if (eventType == XmlPullParser.END_TAG && depth == parentDepth) { + // TODO: this is an example of how to do logging of the XML +// if (mLogTag != null && Log.isLoggable(mLogTag, Log.DEBUG)) { +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < depth; i++) sb.append(" "); +// sb.append("</>"); // Not quite valid xml but it gets the job done. +// Log.d(mLogTag, sb.toString()); +// } + return null; + } + + if (eventType == XmlPullParser.END_DOCUMENT && parentDepth == 0) { + return null; + } + + if (eventType == XmlPullParser.TEXT && depth == parentDepth) { + if (textBuffer == null) { + continue; + } + String text = mParser.getText(); + textBuffer.append(text); + return TEXT_TAG; + } + } + } + + /** + * The same as nextTagOrText(int, StringBuilder) but ignores text blocks. + */ + public String nextTag(int parentDepth) throws IOException, ParseException { + return nextTagOrText(parentDepth, null /* ignore text */); + } + + /** + * Returns the depth of the current element. The depth is 0 before the first + * element has been returned, 1 after that, etc. + * + * @return the depth of the current element + */ + public int getDepth() { + return mParser.getDepth(); + } + + /** + * Consumes the rest of the children, accumulating any text at this level into the builder. + * + * @param textBuffer + * @throws IOException propogated from the XmlPullParser + * @throws ParseException if there was an error parsing the xml. + */ + public void readRemainingText(int parentDepth, StringBuffer textBuffer) + throws IOException, ParseException { + while (nextTagOrText(parentDepth, textBuffer) != null) { + } + } + + /** + * Returns the number of attributes on the current element. + * + * @return the number of attributes on the current element + */ + public int numAttributes() { + return mParser.getAttributeCount(); + } + + /** + * Returns the name of the nth attribute on the current element. + * + * @return the name of the nth attribute on the current element + */ + public String getAttributeName(int i) { + return mParser.getAttributeName(i); + } + + /** + * Returns the namespace of the nth attribute on the current element. + * + * @return the namespace of the nth attribute on the current element + */ + public String getAttributeNamespace(int i) { + return mParser.getAttributeNamespace(i); + } + + /** + * Returns the string value of the named attribute. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute + * @param defaultValue the value to return if the attribute is not specified + * @return the value of the attribute + */ + public String getStringAttribute( + String namespace, String name, String defaultValue) { + String value = mParser.getAttributeValue(namespace, name); + if (null == value) return defaultValue; + return value; + } + + /** + * Returns the string value of the named attribute. An exception will + * be thrown if the attribute is not present. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute @return the value of the attribute + * @throws ParseException thrown if the attribute is missing + */ + public String getStringAttribute(String namespace, String name) throws ParseException { + String value = mParser.getAttributeValue(namespace, name); + if (null == value) { + throw new ParseException( + "missing '" + name + "' attribute on '" + mCurrentStartTag + "' element"); + } + return value; + } + + /** + * Returns the string value of the named attribute. An exception will + * be thrown if the attribute is not a valid integer. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute + * @param defaultValue the value to return if the attribute is not specified + * @return the value of the attribute + * @throws ParseException thrown if the attribute not a valid integer. + */ + public int getIntAttribute(String namespace, String name, int defaultValue) + throws ParseException { + String value = mParser.getAttributeValue(namespace, name); + if (null == value) return defaultValue; + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new ParseException("Cannot parse '" + value + "' as an integer"); + } + } + + /** + * Returns the string value of the named attribute. An exception will + * be thrown if the attribute is not present or is not a valid integer. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute @return the value of the attribute + * @throws ParseException thrown if the attribute is missing or not a valid integer. + */ + public int getIntAttribute(String namespace, String name) + throws ParseException { + String value = getStringAttribute(namespace, name); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new ParseException("Cannot parse '" + value + "' as an integer"); + } + } + + /** + * Returns the string value of the named attribute. An exception will + * be thrown if the attribute is not a valid long. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute @return the value of the attribute + * @throws ParseException thrown if the attribute is not a valid long. + */ + public long getLongAttribute(String namespace, String name, long defaultValue) + throws ParseException { + String value = mParser.getAttributeValue(namespace, name); + if (null == value) return defaultValue; + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + throw new ParseException("Cannot parse '" + value + "' as a long"); + } + } + + /** + * Returns the string value of the named attribute. An exception will + * be thrown if the attribute is not present or is not a valid long. + * + * @param namespace the namespace of the attribute + * @param name the name of the attribute @return the value of the attribute + * @throws ParseException thrown if the attribute is missing or not a valid long. + */ + public long getLongAttribute(String namespace, String name) + throws ParseException { + String value = getStringAttribute(namespace, name); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + throw new ParseException("Cannot parse '" + value + "' as a long"); + } + } + + public static final class ParseException extends Exception { + public ParseException(String message) { + super(message); + } + + public ParseException(String message, Throwable cause) { + super(message, cause); + } + + public ParseException(Throwable cause) { + super(cause); + } + } +} |