XmlPullSubtreeParser

XMLの特定の要素だけをパースしたい場合がある。他のメソッドやクラスにパース処理を委譲してしまいたいのが主な理由だ。

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <array name="preloaded_drawables">
        <item>@drawable/indicator_code_lock_drag_direction_green_up</item>
        <item>@drawable/indicator_code_lock_drag_direction_red_up</item>
    </array>
    <array name="preloaded_color_state_lists">
        <item>@color/hint_foreground_dark</item>
        <item>@color/hint_foreground_light</item>
    </array>
    <integer-array name="maps_starting_lat_lng">
        <item>36149777</item>
        <item>-95993398</item>
    </integer-array>
    <integer-array name="maps_starting_zoom">
        <item>3</item>
    </integer-array>
</resources>

こんなXMLがあったとして、 とその下のXMLツリーだけをビジタやそのメソッドで処理したい場合などだ。

このような場合、サブツリーだけを処理するXMLパーサがあると便利であり.NET C#等では標準のプルパーサに同様の実装がありとても重宝する。残念ながらJavaのプルパーサにはそのような機能は無いが、以前にJava6用のものを作ったことがある。
[Java]無いなら作ろう - XMLStreamSubReader

同様にAndroid用にも作ったが、以前用意したものはXmlResourcePullParserがベースであり広範囲には使いづらいので、改めてXMLPullParserインタフェースをベースに作り直した。

    • XmlPullParserDelegate.java
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;


public class XmlPullParserDelegate implements XmlPullParser {

	protected final XmlPullParser delegate;

	public XmlPullParserDelegate(XmlPullParser delegate) {
		this.delegate = delegate;
	}
	
	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#defineEntityReplacementText(java.lang.String, java.lang.String)
	 */
	@Override
	public void defineEntityReplacementText(String entityName,
			String replacementText) throws XmlPullParserException {
		this.delegate.defineEntityReplacementText(entityName, replacementText);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeCount()
	 */
	@Override
	public int getAttributeCount() {
		return this.delegate.getAttributeCount();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeName(int)
	 */
	@Override
	public String getAttributeName(int index) {
		return this.delegate.getAttributeName(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeNamespace(int)
	 */
	@Override
	public String getAttributeNamespace(int index) {
		return this.delegate.getAttributeNamespace(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributePrefix(int)
	 */
	@Override
	public String getAttributePrefix(int index) {
		return this.delegate.getAttributePrefix(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeType(int)
	 */
	@Override
	public String getAttributeType(int index) {
		return this.delegate.getAttributeType(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeValue(int)
	 */
	@Override
	public String getAttributeValue(int index) {
		return this.delegate.getAttributeValue(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getAttributeValue(java.lang.String, java.lang.String)
	 */
	@Override
	public String getAttributeValue(String namespace, String name) {
		return this.delegate.getAttributeValue(namespace, name);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getColumnNumber()
	 */
	@Override
	public int getColumnNumber() {
		return this.delegate.getColumnNumber();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getDepth()
	 */
	@Override
	public int getDepth() {
		return this.delegate.getDepth();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getEventType()
	 */
	@Override
	public int getEventType() throws XmlPullParserException {
		return this.delegate.getEventType();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getFeature(java.lang.String)
	 */
	@Override
	public boolean getFeature(String name) {
		return this.delegate.getFeature(name);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getInputEncoding()
	 */
	@Override
	public String getInputEncoding() {
		return this.delegate.getInputEncoding();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getLineNumber()
	 */
	@Override
	public int getLineNumber() {
		return this.delegate.getLineNumber();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getName()
	 */
	@Override
	public String getName() {
		return this.delegate.getName();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getNamespace()
	 */
	@Override
	public String getNamespace() {
		return this.delegate.getNamespace();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getNamespace(java.lang.String)
	 */
	@Override
	public String getNamespace(String prefix) {
		return this.delegate.getNamespace(prefix);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getNamespaceCount(int)
	 */
	@Override
	public int getNamespaceCount(int depth) throws XmlPullParserException {
		return this.delegate.getNamespaceCount(depth);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getNamespacePrefix(int)
	 */
	@Override
	public String getNamespacePrefix(int pos) throws XmlPullParserException {
		return this.delegate.getNamespacePrefix(pos);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getNamespaceUri(int)
	 */
	@Override
	public String getNamespaceUri(int pos) throws XmlPullParserException {
		return this.delegate.getNamespaceUri(pos);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getPositionDescription()
	 */
	@Override
	public String getPositionDescription() {
		return this.delegate.getPositionDescription();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getPrefix()
	 */
	@Override
	public String getPrefix() {
		return this.delegate.getPrefix();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getProperty(java.lang.String)
	 */
	@Override
	public Object getProperty(String name) {
		return this.delegate.getProperty(name);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getText()
	 */
	@Override
	public String getText() {
		return this.delegate.getText();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#getTextCharacters(int[])
	 */
	@Override
	public char[] getTextCharacters(int[] holderForStartAndLength) {
		return this.delegate.getTextCharacters(holderForStartAndLength);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#isAttributeDefault(int)
	 */
	@Override
	public boolean isAttributeDefault(int index) {
		return this.delegate.isAttributeDefault(index);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#isEmptyElementTag()
	 */
	@Override
	public boolean isEmptyElementTag() throws XmlPullParserException {
		return this.delegate.isEmptyElementTag();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#isWhitespace()
	 */
	@Override
	public boolean isWhitespace() throws XmlPullParserException {
		return this.delegate.isWhitespace();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#next()
	 */
	@Override
	public int next() throws XmlPullParserException, IOException {
		return this.delegate.next();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#nextTag()
	 */
	@Override
	public int nextTag() throws XmlPullParserException, IOException {
		return this.delegate.nextTag();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#nextText()
	 */
	@Override
	public String nextText() throws XmlPullParserException, IOException {
		return this.delegate.nextText();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#nextToken()
	 */
	@Override
	public int nextToken() throws XmlPullParserException, IOException {
		return this.delegate.nextToken();
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#require(int, java.lang.String, java.lang.String)
	 */
	@Override
	public void require(int type, String namespace, String name)
			throws XmlPullParserException, IOException {
		this.delegate.require(type, namespace, name);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#setFeature(java.lang.String, boolean)
	 */
	@Override
	public void setFeature(String name, boolean state)
			throws XmlPullParserException {
		this.delegate.setFeature(name, state);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#setInput(java.io.Reader)
	 */
	@Override
	public void setInput(Reader in) throws XmlPullParserException {
		this.delegate.setInput(in);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#setInput(java.io.InputStream, java.lang.String)
	 */
	@Override
	public void setInput(InputStream inputStream, String inputEncoding)
			throws XmlPullParserException {
		this.delegate.setInput(inputStream, inputEncoding);
	}

	/* (non-Javadoc)
	 * @see org.xmlpull.v1.XmlPullParser#setProperty(java.lang.String, java.lang.Object)
	 */
	@Override
	public void setProperty(String name, Object value)
			throws XmlPullParserException {
		this.delegate.setProperty(name, value);
	}
}

XmlPullParserDelegateはパーサの処理を委譲するだけだ。

    • XmlPullSubtreeParser.java
import java.io.IOException;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

public class XmlPullSubtreeParser extends XmlPullParserDelegate {
    protected String rootNode;
    protected int rootDepth;
    protected int lastEvent;
    protected int lastDepth;
   
    public XmlPullSubtreeParser(XmlPullParser delegateParser) {
        super(delegateParser);
        this.rootDepth = delegateParser.getDepth();
        this.lastDepth = this.rootDepth;
        this.rootNode = delegateParser.getName();
    }
    public String getRootName() {
        return this.rootNode;
    }
    
    /* (non-Javadoc)
     * @see org.jaffa.util.xml.XmlResourceParserDelegate#next()
     */
    @Override
    public int next() throws XmlPullParserException, IOException {
        if ( this.hasNext() ) {
            this.lastEvent = super.next();
            this.lastDepth = this.getDepth();
            return this.lastEvent;
        } else {
            return 0;
        }
    }
    public boolean hasNext() throws XmlPullParserException {
        if ( this.rootDepth > this.lastDepth ) {
            return false;
        }
        if ( this.lastEvent == XmlPullParser.END_TAG ) {
            if ( this.getName().equals(this.rootNode)) { 
                return false;
            }
            if ( this.rootDepth >= this.lastDepth ) {
                return false;
            }
        }
        
        if ( this.delegate instanceof XmlPullSubtreeParser ) {
            return ((XmlPullSubtreeParser)this.delegate).hasNext();
        } else {
            return (this.lastEvent != XmlPullParser.END_DOCUMENT);
        }
    }
}

XmlPullSubtreeParserはコンストラクタの引数で与えられたXmlPullParserの要素と深度を基準にしてその直下の要素だけをパースする。何をしているかは見れば解るだろう。

※コードは一応テストを通っているがその動作は保証しない。