All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squareup.spoon.axmlparser.AXMLParser Maven / Gradle / Ivy

There is a newer version: 1.7.1
Show newest version
/*
 * Copyright 2008 Android4ME
 *
 * 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.squareup.spoon.axmlparser;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author Dmitry Skiba
 *
 * Parser for Android's binary xml files (axml).
 *
 * TODO:
 * 	* understand ? values
 */
public final class AXMLParser {

    /**
     * Types of returned tags.
     * Values are compatible to those in XmlPullParser.
     */
    public static final int
            START_DOCUMENT 		=0,
            END_DOCUMENT		=1,
            START_TAG			=2,
            END_TAG				=3,
            TEXT				=4;

    /**
     * Creates object and reads file info.
     * Call next() to read first tag.
     */
    public AXMLParser(InputStream stream) throws IOException {
        m_stream=stream;
        doStart();
    }

    /**
     * Closes parser:
     * 	* closes (and nulls) underlying stream
     * 	* nulls dynamic data
     * 	* moves object to 'closed' state, where methods
     * 	  return invalid values and next() throws IOException.
     */
    public final void close() {
        if (m_stream==null) {
            return;
        }
        try {
            m_stream.close();
        }
        catch (IOException e) {
        }
        if (m_nextException==null) {
            m_nextException=new IOException("Closed.");
        }
        m_stream=null;
        resetState();
    }

    /**
     * Advances to the next tag.
     * Once method returns END_DOCUMENT, it always returns END_DOCUMENT.
     * Once method throws an exception, it always throws the same exception.
     *
     */
    public final int next() throws IOException {
        if (m_nextException!=null) {
            throw m_nextException;
        }
        try {
            return doNext();
        }
        catch (IOException e) {
            m_nextException=e;
            resetState();
            throw e;
        }
    }

    /**
     * Returns current tag type.
     */
    public final int getType() {
        return m_tagType;
    }

    /**
     * Returns name for the current tag.
     */
    public final String getName() {
        if (m_tagName==-1) {
            return null;
        }
        return getString(m_tagName);
    }

    /**
     * Returns line number in the original XML where the current tag was.
     */
    public final int getLineNumber() {
        return m_tagSourceLine;
    }

    /**
     * Returns count of attributes for the current tag.
     */
    public final int getAttributeCount() {
        if (m_tagAttributes==null) {
            return -1;
        }
        return m_tagAttributes.length;
    }

    /**
     * Returns attribute namespace.
     */
    public final String getAttributeNamespace(int index) {
        return getString(getAttribute(index).namespace);
    }

    /**
     * Returns attribute name.
     */
    public final String getAttributeName(int index) {
        return getString(getAttribute(index).name);
    }

    /**
     * Returns attribute resource ID.
     */
    public final int getAttributeResourceID(int index) {
        int resourceIndex=getAttribute(index).name;
        if (m_resourceIDs==null ||
                resourceIndex<0 || resourceIndex>=m_resourceIDs.length)
        {
            return 0;
        }
        return m_resourceIDs[resourceIndex];
    }

    /**
     * Returns type of attribute value.
     * See TypedValue.TYPE_ values.
     */
    public final int getAttributeValueType(int index) {
        return getAttribute(index).valueType;
    }

    /**
     * For attributes of type TypedValue.TYPE_STRING returns
     *  string value. For other types returns empty string.
     */
    public final String getAttributeValueString(int index) {
        return getString(getAttribute(index).valueString);
    }

    /**
     * Returns integer attribute value.
     * This integer interpreted according to attribute type.
     */
    public final int getAttributeValue(int index) {
        return getAttribute(index).value;
    }

    ///////////////////////////////////////////// implementation

    private static final class TagAttribute {
        public int namespace;
        public int name;
        public int valueString;
        public int valueType;
        public int value;
    }

    private final void resetState() {
        m_tagType=-1;
        m_tagSourceLine=-1;
        m_tagName=-1;
        m_tagAttributes=null;
    }

    private final void doStart() throws IOException {
        ReadUtil.readCheckType(m_stream,AXML_CHUNK_TYPE);
        /*chunk size*/ReadUtil.readInt(m_stream);

        m_strings=StringBlock.read(new IntReader(m_stream,false));

        ReadUtil.readCheckType(m_stream,RESOURCEIDS_CHUNK_TYPE);
        int chunkSize=ReadUtil.readInt(m_stream);
        if (chunkSize<8 || (chunkSize%4)!=0) {
            throw new IOException("Invalid resource ids size ("+chunkSize+").");
        }
        m_resourceIDs=ReadUtil.readIntArray(m_stream,chunkSize/4-2);

        resetState();
    }

    private final int doNext() throws IOException {
        if (m_tagType==END_DOCUMENT) {
            return END_DOCUMENT;
        }

        m_tagType=(ReadUtil.readInt(m_stream) & 0xFF);/*other 3 bytes?*/
        /*some source length*/ReadUtil.readInt(m_stream);
        m_tagSourceLine=ReadUtil.readInt(m_stream);
        /*0xFFFFFFFF*/ReadUtil.readInt(m_stream);

        m_tagName=-1;
        m_tagAttributes=null;

        switch (m_tagType) {
            case START_DOCUMENT:
            {
                /*namespace?*/ReadUtil.readInt(m_stream);
                /*name?*/ReadUtil.readInt(m_stream);
                break;
            }
            case START_TAG:
            {
                /*0xFFFFFFFF*/ReadUtil.readInt(m_stream);
                m_tagName=ReadUtil.readInt(m_stream);
                /*flags?*/ReadUtil.readInt(m_stream);
                int attributeCount=ReadUtil.readInt(m_stream);
                /*?*/ReadUtil.readInt(m_stream);
                m_tagAttributes=new TagAttribute[attributeCount];
                for (int i=0;i!=attributeCount;++i) {
                    TagAttribute attribute=new TagAttribute();
                    attribute.namespace=ReadUtil.readInt(m_stream);
                    attribute.name=ReadUtil.readInt(m_stream);
                    attribute.valueString=ReadUtil.readInt(m_stream);
                    attribute.valueType=(ReadUtil.readInt(m_stream)>>>24);/*other 3 bytes?*/
                    attribute.value=ReadUtil.readInt(m_stream);
                    m_tagAttributes[i]=attribute;
                }
                break;
            }
            case END_TAG:
            {
                /*0xFFFFFFFF*/ReadUtil.readInt(m_stream);
                m_tagName=ReadUtil.readInt(m_stream);
                break;
            }
            case TEXT:
            {
                m_tagName=ReadUtil.readInt(m_stream);
                /*?*/ReadUtil.readInt(m_stream);
                /*?*/ReadUtil.readInt(m_stream);
                break;
            }
            case END_DOCUMENT:
            {
                /*namespace?*/ReadUtil.readInt(m_stream);
                /*name?*/ReadUtil.readInt(m_stream);
                break;
            }
            default:
            {
                throw new IOException("Invalid tag type ("+m_tagType+").");
            }
        }
        return m_tagType;
    }

    private final TagAttribute getAttribute(int index) {
        if (m_tagAttributes==null) {
            throw new IndexOutOfBoundsException("Attributes are not available.");
        }
        if (index>=m_tagAttributes.length) {
            throw new IndexOutOfBoundsException("Invalid attribute index ("+index+").");
        }
        return m_tagAttributes[index];
    }

    private final String getString(int index) {
        if (index==-1) {
            return "";
        }
        return m_strings.getRaw(index);
    }

    /////////////////////////////////// data

    private InputStream m_stream;

    private StringBlock m_strings;
    private int[] m_resourceIDs;

    private IOException m_nextException;

    private int m_tagType;
    private int m_tagSourceLine;
    private int m_tagName;
    private TagAttribute[] m_tagAttributes;

    private static final int
            AXML_CHUNK_TYPE			=0x00080003,
            RESOURCEIDS_CHUNK_TYPE	=0x00080180;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy