From 20817df69f8d807526bd4f101d510dca7173c164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Wi=C5=9Bniewski?= Date: Tue, 23 Mar 2010 12:15:39 +0100 Subject: [PATCH] StringBlock, AXmlResourceParser: reformat. --- .../res/decoder/AXmlResourceParser.java | 1814 ++++++++--------- .../androlib/res/decoder/StringBlock.java | 484 ++--- 2 files changed, 1149 insertions(+), 1149 deletions(-) diff --git a/src/brut/androlib/res/decoder/AXmlResourceParser.java b/src/brut/androlib/res/decoder/AXmlResourceParser.java index 6eb7f425..09922758 100644 --- a/src/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/src/brut/androlib/res/decoder/AXmlResourceParser.java @@ -1,907 +1,907 @@ -/* - * Copyright 2010 Ryszard Wiśniewski . - * - * 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. - * under the License. - */ -package brut.androlib.res.decoder; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import org.xmlpull.v1.XmlPullParserException; -import android.util.TypedValue; - -/** - * @author Ryszard Wiśniewski - * @author Dmitry Skiba - * - * Binary xml files parser. - * - * Parser has only two states: - * (1) Operational state, which parser obtains after first successful call - * to next() and retains until open(), close(), or failed call to next(). - * (2) Closed state, which parser obtains after open(), close(), or failed - * call to next(). In this state methods return invalid values or throw exceptions. - * - * TODO: - * * check all methods in closed state - * - */ -public class AXmlResourceParser implements XmlResourceParser { - - public AXmlResourceParser() { - resetEventInfo(); - } - - public void open(InputStream stream) { - close(); - if (stream != null) { - m_reader = new IntReader(stream, false); - } - } - - public void close() { - if (!m_operational) { - return; - } - m_operational = false; - m_reader.close(); - m_reader = null; - m_strings = null; - m_resourceIDs = null; - m_namespaces.reset(); - resetEventInfo(); - } - - /////////////////////////////////// iteration - public int next() throws XmlPullParserException, IOException { - if (m_reader == null) { - throw new XmlPullParserException("Parser is not opened.", this, null); - } - try { - doNext(); - return m_event; - } catch (IOException e) { - close(); - throw e; - } - } - - public int nextToken() throws XmlPullParserException, IOException { - return next(); - } - - public int nextTag() throws XmlPullParserException, IOException { - int eventType = next(); - if (eventType == TEXT && isWhitespace()) { - eventType = next(); - } - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("Expected start or end tag.", this, null); - } - return eventType; - } - - public String nextText() throws XmlPullParserException, IOException { - if (getEventType() != START_TAG) { - throw new XmlPullParserException("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("Event TEXT must be immediately followed by END_TAG.", this, null); - } - return result; - } else if (eventType == END_TAG) { - return ""; - } else { - throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null); - } - } - - public void require(int type, String namespace, String name) throws XmlPullParserException, IOException { - if (type != getEventType() - || (namespace != null && !namespace.equals(getNamespace())) - || (name != null && !name.equals(getName()))) { - throw new XmlPullParserException(TYPES[type] + " is expected.", this, null); - } - } - - public int getDepth() { - return m_namespaces.getDepth() - 1; - } - - public int getEventType() throws XmlPullParserException { - return m_event; - } - - public int getLineNumber() { - return m_lineNumber; - } - - public String getName() { - if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { - return null; - } - return m_strings.getString(m_name); - } - - public String getText() { - if (m_name == -1 || m_event != TEXT) { - return null; - } - return m_strings.getString(m_name); - } - - public char[] getTextCharacters(int[] holderForStartAndLength) { - String text = getText(); - if (text == null) { - return null; - } - holderForStartAndLength[0] = 0; - holderForStartAndLength[1] = text.length(); - char[] chars = new char[text.length()]; - text.getChars(0, text.length(), chars, 0); - return chars; - } - - public String getNamespace() { - return m_strings.getString(m_namespaceUri); - } - - public String getPrefix() { - int prefix = m_namespaces.findPrefix(m_namespaceUri); - return m_strings.getString(prefix); - } - - public String getPositionDescription() { - return "XML line #" + getLineNumber(); - } - - public int getNamespaceCount(int depth) throws XmlPullParserException { - return m_namespaces.getAccumulatedCount(depth); - } - - public String getNamespacePrefix(int pos) throws XmlPullParserException { - int prefix = m_namespaces.getPrefix(pos); - return m_strings.getString(prefix); - } - - public String getNamespaceUri(int pos) throws XmlPullParserException { - int uri = m_namespaces.getUri(pos); - return m_strings.getString(uri); - } - - /////////////////////////////////// attributes - public String getClassAttribute() { - if (m_classAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_classAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public String getIdAttribute() { - if (m_idAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_idAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public int getIdAttributeResourceValue(int defaultValue) { - if (m_idAttribute == -1) { - return defaultValue; - } - int offset = getAttributeOffset(m_idAttribute); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType != TypedValue.TYPE_REFERENCE) { - return defaultValue; - } - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getStyleAttribute() { - if (m_styleAttribute == -1) { - return 0; - } - int offset = getAttributeOffset(m_styleAttribute); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getAttributeCount() { - if (m_event != START_TAG) { - return -1; - } - return m_attributes.length / ATTRIBUTE_LENGHT; - } - - public String getAttributeNamespace(int index) { - int offset = getAttributeOffset(index); - int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - if (namespace == -1) { - return ""; - } - return m_strings.getString(namespace); - } - - public String getAttributePrefix(int index) { - int offset = getAttributeOffset(index); - int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - int prefix = m_namespaces.findPrefix(uri); - if (prefix == -1) { - return ""; - } - return m_strings.getString(prefix); - } - - public String getAttributeName(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (name == -1) { - return ""; - } - return m_strings.getString(name); - } - - public int getAttributeNameResource(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (m_resourceIDs == null - || name < 0 || name >= m_resourceIDs.length) { - return 0; - } - return m_resourceIDs[name]; - } - - public int getAttributeValueType(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - } - - public int getAttributeValueData(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public String getAttributeValue(int index) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_STRING) { - int valueString = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(valueString); - } - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return "";//TypedValue.coerceToString(valueType,valueData); - } - - public boolean getAttributeBooleanValue(int index, boolean defaultValue) { - return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; - } - - public float getAttributeFloatValue(int index, float defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_FLOAT) { - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return Float.intBitsToFloat(valueData); - } - return defaultValue; - } - - public int getAttributeIntValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType >= TypedValue.TYPE_FIRST_INT - && valueType <= TypedValue.TYPE_LAST_INT) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public int getAttributeUnsignedIntValue(int index, int defaultValue) { - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_REFERENCE) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public String getAttributeValue(String namespace, String attribute) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return null; - } - return getAttributeValue(index); - } - - public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeBooleanValue(index, defaultValue); - } - - public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeFloatValue(index, defaultValue); - } - - public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeUnsignedIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeResourceValue(index, defaultValue); - } - - public int getAttributeListValue(int index, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public String getAttributeType(int index) { - return "CDATA"; - } - - public boolean isAttributeDefault(int index) { - return false; - } - - /////////////////////////////////// dummies - public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public void setInput(Reader reader) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getInputEncoding() { - return null; - } - - public int getColumnNumber() { - return -1; - } - - public boolean isEmptyElementTag() throws XmlPullParserException { - return false; - } - - public boolean isWhitespace() throws XmlPullParserException { - return false; - } - - public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getNamespace(String prefix) { - throw new RuntimeException(E_NOT_SUPPORTED); - } - - public Object getProperty(String name) { - return null; - } - - public void setProperty(String name, Object value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public boolean getFeature(String feature) { - return false; - } - - public void setFeature(String name, boolean value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - ///////////////////////////////////////////// implementation - /** - * Namespace stack, holds prefix+uri pairs, as well as - * depth information. - * All information is stored in one int[] array. - * Array consists of depth frames: - * Data=DepthFrame*; - * DepthFrame=Count+[Prefix+Uri]*+Count; - * Count='count of Prefix+Uri pairs'; - * Yes, count is stored twice, to enable bottom-up traversal. - * increaseDepth adds depth frame, decreaseDepth removes it. - * push/pop operations operate only in current depth frame. - * decreaseDepth removes any remaining (not pop'ed) namespace pairs. - * findXXX methods search all depth frames starting - * from the last namespace pair of current depth frame. - * All functions that operate with int, use -1 as 'invalid value'. - * - * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! - * - */ - private static final class NamespaceStack { - - public NamespaceStack() { - m_data = new int[32]; - } - - public final void reset() { - m_dataLength = 0; - m_count = 0; - m_depth = 0; - } - - public final int getTotalCount() { - return m_count; - } - - public final int getCurrentCount() { - if (m_dataLength == 0) { - return 0; - } - int offset = m_dataLength - 1; - return m_data[offset]; - } - - public final int getAccumulatedCount(int depth) { - if (m_dataLength == 0 || depth < 0) { - return 0; - } - if (depth > m_depth) { - depth = m_depth; - } - int accumulatedCount = 0; - int offset = 0; - for (; depth != 0; --depth) { - int count = m_data[offset]; - accumulatedCount += count; - offset += (2 + count * 2); - } - return accumulatedCount; - } - - public final void push(int prefix, int uri) { - if (m_depth == 0) { - increaseDepth(); - } - ensureDataCapacity(2); - int offset = m_dataLength - 1; - int count = m_data[offset]; - m_data[offset - 1 - count * 2] = count + 1; - m_data[offset] = prefix; - m_data[offset + 1] = uri; - m_data[offset + 2] = count + 1; - m_dataLength += 2; - m_count += 1; - } - - public final boolean pop(int prefix, int uri) { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { - if (m_data[o] != prefix || m_data[o + 1] != uri) { - continue; - } - count -= 1; - if (i == 0) { - m_data[o] = count; - o -= (1 + count * 2); - m_data[o] = count; - } else { - m_data[offset] = count; - offset -= (1 + 2 + count * 2); - m_data[offset] = count; - System.arraycopy( - m_data, o + 2, - m_data, o, - m_dataLength - o); - } - m_dataLength -= 2; - m_count -= 1; - return true; - } - return false; - } - - public final boolean pop() { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if (count == 0) { - return false; - } - count -= 1; - offset -= 2; - m_data[offset] = count; - offset -= (1 + count * 2); - m_data[offset] = count; - m_dataLength -= 2; - m_count -= 1; - return true; - } - - public final int getPrefix(int index) { - return get(index, true); - } - - public final int getUri(int index) { - return get(index, false); - } - - public final int findPrefix(int uri) { - return find(uri, false); - } - - public final int findUri(int prefix) { - return find(prefix, true); - } - - public final int getDepth() { - return m_depth; - } - - public final void increaseDepth() { - ensureDataCapacity(2); - int offset = m_dataLength; - m_data[offset] = 0; - m_data[offset + 1] = 0; - m_dataLength += 2; - m_depth += 1; - } - - public final void decreaseDepth() { - if (m_dataLength == 0) { - return; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if ((offset - 1 - count * 2) == 0) { - return; - } - m_dataLength -= 2 + count * 2; - m_count -= count; - m_depth -= 1; - } - - private void ensureDataCapacity(int capacity) { - int available = (m_data.length - m_dataLength); - if (available > capacity) { - return; - } - int newLength = (m_data.length + available) * 2; - int[] newData = new int[newLength]; - System.arraycopy(m_data, 0, newData, 0, m_dataLength); - m_data = newData; - } - - private final int find(int prefixOrUri, boolean prefix) { - if (m_dataLength == 0) { - return -1; - } - int offset = m_dataLength - 1; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - offset -= 2; - for (; count != 0; --count) { - if (prefix) { - if (m_data[offset] == prefixOrUri) { - return m_data[offset + 1]; - } - } else { - if (m_data[offset + 1] == prefixOrUri) { - return m_data[offset]; - } - } - offset -= 2; - } - } - return -1; - } - - private final int get(int index, boolean prefix) { - if (m_dataLength == 0 || index < 0) { - return -1; - } - int offset = 0; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - if (index >= count) { - index -= count; - offset += (2 + count * 2); - continue; - } - offset += (1 + index * 2); - if (!prefix) { - offset += 1; - } - return m_data[offset]; - } - return -1; - } - private int[] m_data; - private int m_dataLength; - private int m_count; - private int m_depth; - } - - /////////////////////////////////// package-visible -// final void fetchAttributes(int[] styleableIDs,TypedArray result) { -// result.resetIndices(); -// if (m_attributes==null || m_resourceIDs==null) { -// return; -// } -// boolean needStrings=false; -// for (int i=0,e=styleableIDs.length;i!=e;++i) { -// int id=styleableIDs[i]; -// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { -// int name=m_attributes[o+ATTRIBUTE_IX_NAME]; -// if (name>=m_resourceIDs.length || -// m_resourceIDs[name]!=id) -// { -// continue; -// } -// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; -// int valueData; -// int assetCookie; -// if (valueType==TypedValue.TYPE_STRING) { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; -// assetCookie=-1; -// needStrings=true; -// } else { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; -// assetCookie=0; -// } -// result.addValue(i,valueType,valueData,assetCookie,id,0); -// } -// } -// if (needStrings) { -// result.setStrings(m_strings); -// } -// } - final StringBlock getStrings() { - return m_strings; - } - - /////////////////////////////////// - private final int getAttributeOffset(int index) { - if (m_event != START_TAG) { - throw new IndexOutOfBoundsException("Current event is not START_TAG."); - } - int offset = index * 5; - if (offset >= m_attributes.length) { - throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ")."); - } - return offset; - } - - private final int findAttribute(String namespace, String attribute) { - if (m_strings == null || attribute == null) { - return -1; - } - int name = m_strings.find(attribute); - if (name == -1) { - return -1; - } - int uri = (namespace != null) - ? m_strings.find(namespace) - : -1; - for (int o = 0; o != m_attributes.length; ++o) { - if (name == m_attributes[o + ATTRIBUTE_IX_NAME] - && (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) { - return o / ATTRIBUTE_LENGHT; - } - } - return -1; - } - - private final void resetEventInfo() { - m_event = -1; - m_lineNumber = -1; - m_name = -1; - m_namespaceUri = -1; - m_attributes = null; - m_idAttribute = -1; - m_classAttribute = -1; - m_styleAttribute = -1; - } - - private final void doNext() throws IOException { - // Delayed initialization. - if (m_strings == null) { - ChunkUtil.readCheckType(m_reader, CHUNK_AXML_FILE); - /*chunkSize*/ m_reader.skipInt(); - m_strings = StringBlock.read(m_reader); - m_namespaces.increaseDepth(); - m_operational = true; - } - - if (m_event == END_DOCUMENT) { - return; - } - - int event = m_event; - resetEventInfo(); - - while (true) { - if (m_decreaseDepth) { - m_decreaseDepth = false; - m_namespaces.decreaseDepth(); - } - - // Fake END_DOCUMENT event. - if (event == END_TAG - && m_namespaces.getDepth() == 1 - && m_namespaces.getCurrentCount() == 0) { - m_event = END_DOCUMENT; - break; - } - - int chunkType; - if (event == START_DOCUMENT) { - // Fake event, see CHUNK_XML_START_TAG handler. - chunkType = CHUNK_XML_START_TAG; - } else { - chunkType = m_reader.readInt(); - } - - if (chunkType == CHUNK_RESOURCEIDS) { - int chunkSize = m_reader.readInt(); - if (chunkSize < 8 || (chunkSize % 4) != 0) { - throw new IOException("Invalid resource ids size (" + chunkSize + ")."); - } - m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); - continue; - } - - if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { - throw new IOException("Invalid chunk type (" + chunkType + ")."); - } - - // Fake START_DOCUMENT event. - if (chunkType == CHUNK_XML_START_TAG && event == -1) { - m_event = START_DOCUMENT; - break; - } - - // Common header. - /*chunkSize*/ m_reader.skipInt(); - int lineNumber = m_reader.readInt(); - /*0xFFFFFFFF*/ m_reader.skipInt(); - - if (chunkType == CHUNK_XML_START_NAMESPACE - || chunkType == CHUNK_XML_END_NAMESPACE) { - if (chunkType == CHUNK_XML_START_NAMESPACE) { - int prefix = m_reader.readInt(); - int uri = m_reader.readInt(); - m_namespaces.push(prefix, uri); - } else { - /*prefix*/ m_reader.skipInt(); - /*uri*/ m_reader.skipInt(); - m_namespaces.pop(); - } - continue; - } - - m_lineNumber = lineNumber; - - if (chunkType == CHUNK_XML_START_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - /*flags?*/ m_reader.skipInt(); - int attributeCount = m_reader.readInt(); - m_idAttribute = (attributeCount >>> 16) - 1; - attributeCount &= 0xFFFF; - m_classAttribute = m_reader.readInt(); - m_styleAttribute = (m_classAttribute >>> 16) - 1; - m_classAttribute = (m_classAttribute & 0xFFFF) - 1; - m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT); - for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { - m_attributes[i] = (m_attributes[i] >>> 24); - i += ATTRIBUTE_LENGHT; - } - m_namespaces.increaseDepth(); - m_event = START_TAG; - break; - } - - if (chunkType == CHUNK_XML_END_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - m_event = END_TAG; - m_decreaseDepth = true; - break; - } - - if (chunkType == CHUNK_XML_TEXT) { - m_name = m_reader.readInt(); - /*?*/ m_reader.skipInt(); - /*?*/ m_reader.skipInt(); - m_event = TEXT; - break; - } - } - } - /////////////////////////////////// data - /* - * All values are essentially indices, e.g. m_name is - * an index of name in m_strings. - */ - private IntReader m_reader; - private boolean m_operational = false; - private StringBlock m_strings; - private int[] m_resourceIDs; - private NamespaceStack m_namespaces = new NamespaceStack(); - private boolean m_decreaseDepth; - private int m_event; - private int m_lineNumber; - private int m_name; - private int m_namespaceUri; - private int[] m_attributes; - private int m_idAttribute; - private int m_classAttribute; - private int m_styleAttribute; - private static final String E_NOT_SUPPORTED = "Method is not supported."; - private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0, - ATTRIBUTE_IX_NAME = 1, - ATTRIBUTE_IX_VALUE_STRING = 2, - ATTRIBUTE_IX_VALUE_TYPE = 3, - ATTRIBUTE_IX_VALUE_DATA = 4, - ATTRIBUTE_LENGHT = 5; - private static final int CHUNK_AXML_FILE = 0x00080003, - CHUNK_RESOURCEIDS = 0x00080180, - CHUNK_XML_FIRST = 0x00100100, - CHUNK_XML_START_NAMESPACE = 0x00100100, - CHUNK_XML_END_NAMESPACE = 0x00100101, - CHUNK_XML_START_TAG = 0x00100102, - CHUNK_XML_END_TAG = 0x00100103, - CHUNK_XML_TEXT = 0x00100104, - CHUNK_XML_LAST = 0x00100104; -} +/* + * Copyright 2010 Ryszard Wiśniewski . + * + * 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. + * under the License. + */ +package brut.androlib.res.decoder; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import org.xmlpull.v1.XmlPullParserException; +import android.util.TypedValue; + +/** + * @author Ryszard Wiśniewski + * @author Dmitry Skiba + * + * Binary xml files parser. + * + * Parser has only two states: + * (1) Operational state, which parser obtains after first successful call + * to next() and retains until open(), close(), or failed call to next(). + * (2) Closed state, which parser obtains after open(), close(), or failed + * call to next(). In this state methods return invalid values or throw exceptions. + * + * TODO: + * * check all methods in closed state + * + */ +public class AXmlResourceParser implements XmlResourceParser { + + public AXmlResourceParser() { + resetEventInfo(); + } + + public void open(InputStream stream) { + close(); + if (stream != null) { + m_reader = new IntReader(stream, false); + } + } + + public void close() { + if (!m_operational) { + return; + } + m_operational = false; + m_reader.close(); + m_reader = null; + m_strings = null; + m_resourceIDs = null; + m_namespaces.reset(); + resetEventInfo(); + } + + /////////////////////////////////// iteration + public int next() throws XmlPullParserException, IOException { + if (m_reader == null) { + throw new XmlPullParserException("Parser is not opened.", this, null); + } + try { + doNext(); + return m_event; + } catch (IOException e) { + close(); + throw e; + } + } + + public int nextToken() throws XmlPullParserException, IOException { + return next(); + } + + public int nextTag() throws XmlPullParserException, IOException { + int eventType = next(); + if (eventType == TEXT && isWhitespace()) { + eventType = next(); + } + if (eventType != START_TAG && eventType != END_TAG) { + throw new XmlPullParserException("Expected start or end tag.", this, null); + } + return eventType; + } + + public String nextText() throws XmlPullParserException, IOException { + if (getEventType() != START_TAG) { + throw new XmlPullParserException("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("Event TEXT must be immediately followed by END_TAG.", this, null); + } + return result; + } else if (eventType == END_TAG) { + return ""; + } else { + throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null); + } + } + + public void require(int type, String namespace, String name) throws XmlPullParserException, IOException { + if (type != getEventType() + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) { + throw new XmlPullParserException(TYPES[type] + " is expected.", this, null); + } + } + + public int getDepth() { + return m_namespaces.getDepth() - 1; + } + + public int getEventType() throws XmlPullParserException { + return m_event; + } + + public int getLineNumber() { + return m_lineNumber; + } + + public String getName() { + if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { + return null; + } + return m_strings.getString(m_name); + } + + public String getText() { + if (m_name == -1 || m_event != TEXT) { + return null; + } + return m_strings.getString(m_name); + } + + public char[] getTextCharacters(int[] holderForStartAndLength) { + String text = getText(); + if (text == null) { + return null; + } + holderForStartAndLength[0] = 0; + holderForStartAndLength[1] = text.length(); + char[] chars = new char[text.length()]; + text.getChars(0, text.length(), chars, 0); + return chars; + } + + public String getNamespace() { + return m_strings.getString(m_namespaceUri); + } + + public String getPrefix() { + int prefix = m_namespaces.findPrefix(m_namespaceUri); + return m_strings.getString(prefix); + } + + public String getPositionDescription() { + return "XML line #" + getLineNumber(); + } + + public int getNamespaceCount(int depth) throws XmlPullParserException { + return m_namespaces.getAccumulatedCount(depth); + } + + public String getNamespacePrefix(int pos) throws XmlPullParserException { + int prefix = m_namespaces.getPrefix(pos); + return m_strings.getString(prefix); + } + + public String getNamespaceUri(int pos) throws XmlPullParserException { + int uri = m_namespaces.getUri(pos); + return m_strings.getString(uri); + } + + /////////////////////////////////// attributes + public String getClassAttribute() { + if (m_classAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_classAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + public String getIdAttribute() { + if (m_idAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_idAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + public int getIdAttributeResourceValue(int defaultValue) { + if (m_idAttribute == -1) { + return defaultValue; + } + int offset = getAttributeOffset(m_idAttribute); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType != TypedValue.TYPE_REFERENCE) { + return defaultValue; + } + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + public int getStyleAttribute() { + if (m_styleAttribute == -1) { + return 0; + } + int offset = getAttributeOffset(m_styleAttribute); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + public int getAttributeCount() { + if (m_event != START_TAG) { + return -1; + } + return m_attributes.length / ATTRIBUTE_LENGHT; + } + + public String getAttributeNamespace(int index) { + int offset = getAttributeOffset(index); + int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + if (namespace == -1) { + return ""; + } + return m_strings.getString(namespace); + } + + public String getAttributePrefix(int index) { + int offset = getAttributeOffset(index); + int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + int prefix = m_namespaces.findPrefix(uri); + if (prefix == -1) { + return ""; + } + return m_strings.getString(prefix); + } + + public String getAttributeName(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (name == -1) { + return ""; + } + return m_strings.getString(name); + } + + public int getAttributeNameResource(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (m_resourceIDs == null + || name < 0 || name >= m_resourceIDs.length) { + return 0; + } + return m_resourceIDs[name]; + } + + public int getAttributeValueType(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + } + + public int getAttributeValueData(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + public String getAttributeValue(int index) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_STRING) { + int valueString = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(valueString); + } + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + return "";//TypedValue.coerceToString(valueType,valueData); + } + + public boolean getAttributeBooleanValue(int index, boolean defaultValue) { + return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; + } + + public float getAttributeFloatValue(int index, float defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_FLOAT) { + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + return Float.intBitsToFloat(valueData); + } + return defaultValue; + } + + public int getAttributeIntValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType >= TypedValue.TYPE_FIRST_INT + && valueType <= TypedValue.TYPE_LAST_INT) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + public int getAttributeUnsignedIntValue(int index, int defaultValue) { + return getAttributeIntValue(index, defaultValue); + } + + public int getAttributeResourceValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_REFERENCE) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + public String getAttributeValue(String namespace, String attribute) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return null; + } + return getAttributeValue(index); + } + + public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeBooleanValue(index, defaultValue); + } + + public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeFloatValue(index, defaultValue); + } + + public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeIntValue(index, defaultValue); + } + + public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeUnsignedIntValue(index, defaultValue); + } + + public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeResourceValue(index, defaultValue); + } + + public int getAttributeListValue(int index, String[] options, int defaultValue) { + // TODO implement + return 0; + } + + public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) { + // TODO implement + return 0; + } + + public String getAttributeType(int index) { + return "CDATA"; + } + + public boolean isAttributeDefault(int index) { + return false; + } + + /////////////////////////////////// dummies + public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + public void setInput(Reader reader) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + public String getInputEncoding() { + return null; + } + + public int getColumnNumber() { + return -1; + } + + public boolean isEmptyElementTag() throws XmlPullParserException { + return false; + } + + public boolean isWhitespace() throws XmlPullParserException { + return false; + } + + public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + public String getNamespace(String prefix) { + throw new RuntimeException(E_NOT_SUPPORTED); + } + + public Object getProperty(String name) { + return null; + } + + public void setProperty(String name, Object value) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + public boolean getFeature(String feature) { + return false; + } + + public void setFeature(String name, boolean value) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + ///////////////////////////////////////////// implementation + /** + * Namespace stack, holds prefix+uri pairs, as well as + * depth information. + * All information is stored in one int[] array. + * Array consists of depth frames: + * Data=DepthFrame*; + * DepthFrame=Count+[Prefix+Uri]*+Count; + * Count='count of Prefix+Uri pairs'; + * Yes, count is stored twice, to enable bottom-up traversal. + * increaseDepth adds depth frame, decreaseDepth removes it. + * push/pop operations operate only in current depth frame. + * decreaseDepth removes any remaining (not pop'ed) namespace pairs. + * findXXX methods search all depth frames starting + * from the last namespace pair of current depth frame. + * All functions that operate with int, use -1 as 'invalid value'. + * + * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! + * + */ + private static final class NamespaceStack { + + public NamespaceStack() { + m_data = new int[32]; + } + + public final void reset() { + m_dataLength = 0; + m_count = 0; + m_depth = 0; + } + + public final int getTotalCount() { + return m_count; + } + + public final int getCurrentCount() { + if (m_dataLength == 0) { + return 0; + } + int offset = m_dataLength - 1; + return m_data[offset]; + } + + public final int getAccumulatedCount(int depth) { + if (m_dataLength == 0 || depth < 0) { + return 0; + } + if (depth > m_depth) { + depth = m_depth; + } + int accumulatedCount = 0; + int offset = 0; + for (; depth != 0; --depth) { + int count = m_data[offset]; + accumulatedCount += count; + offset += (2 + count * 2); + } + return accumulatedCount; + } + + public final void push(int prefix, int uri) { + if (m_depth == 0) { + increaseDepth(); + } + ensureDataCapacity(2); + int offset = m_dataLength - 1; + int count = m_data[offset]; + m_data[offset - 1 - count * 2] = count + 1; + m_data[offset] = prefix; + m_data[offset + 1] = uri; + m_data[offset + 2] = count + 1; + m_dataLength += 2; + m_count += 1; + } + + public final boolean pop(int prefix, int uri) { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { + if (m_data[o] != prefix || m_data[o + 1] != uri) { + continue; + } + count -= 1; + if (i == 0) { + m_data[o] = count; + o -= (1 + count * 2); + m_data[o] = count; + } else { + m_data[offset] = count; + offset -= (1 + 2 + count * 2); + m_data[offset] = count; + System.arraycopy( + m_data, o + 2, + m_data, o, + m_dataLength - o); + } + m_dataLength -= 2; + m_count -= 1; + return true; + } + return false; + } + + public final boolean pop() { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if (count == 0) { + return false; + } + count -= 1; + offset -= 2; + m_data[offset] = count; + offset -= (1 + count * 2); + m_data[offset] = count; + m_dataLength -= 2; + m_count -= 1; + return true; + } + + public final int getPrefix(int index) { + return get(index, true); + } + + public final int getUri(int index) { + return get(index, false); + } + + public final int findPrefix(int uri) { + return find(uri, false); + } + + public final int findUri(int prefix) { + return find(prefix, true); + } + + public final int getDepth() { + return m_depth; + } + + public final void increaseDepth() { + ensureDataCapacity(2); + int offset = m_dataLength; + m_data[offset] = 0; + m_data[offset + 1] = 0; + m_dataLength += 2; + m_depth += 1; + } + + public final void decreaseDepth() { + if (m_dataLength == 0) { + return; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if ((offset - 1 - count * 2) == 0) { + return; + } + m_dataLength -= 2 + count * 2; + m_count -= count; + m_depth -= 1; + } + + private void ensureDataCapacity(int capacity) { + int available = (m_data.length - m_dataLength); + if (available > capacity) { + return; + } + int newLength = (m_data.length + available) * 2; + int[] newData = new int[newLength]; + System.arraycopy(m_data, 0, newData, 0, m_dataLength); + m_data = newData; + } + + private final int find(int prefixOrUri, boolean prefix) { + if (m_dataLength == 0) { + return -1; + } + int offset = m_dataLength - 1; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + offset -= 2; + for (; count != 0; --count) { + if (prefix) { + if (m_data[offset] == prefixOrUri) { + return m_data[offset + 1]; + } + } else { + if (m_data[offset + 1] == prefixOrUri) { + return m_data[offset]; + } + } + offset -= 2; + } + } + return -1; + } + + private final int get(int index, boolean prefix) { + if (m_dataLength == 0 || index < 0) { + return -1; + } + int offset = 0; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + if (index >= count) { + index -= count; + offset += (2 + count * 2); + continue; + } + offset += (1 + index * 2); + if (!prefix) { + offset += 1; + } + return m_data[offset]; + } + return -1; + } + private int[] m_data; + private int m_dataLength; + private int m_count; + private int m_depth; + } + + /////////////////////////////////// package-visible +// final void fetchAttributes(int[] styleableIDs,TypedArray result) { +// result.resetIndices(); +// if (m_attributes==null || m_resourceIDs==null) { +// return; +// } +// boolean needStrings=false; +// for (int i=0,e=styleableIDs.length;i!=e;++i) { +// int id=styleableIDs[i]; +// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { +// int name=m_attributes[o+ATTRIBUTE_IX_NAME]; +// if (name>=m_resourceIDs.length || +// m_resourceIDs[name]!=id) +// { +// continue; +// } +// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; +// int valueData; +// int assetCookie; +// if (valueType==TypedValue.TYPE_STRING) { +// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; +// assetCookie=-1; +// needStrings=true; +// } else { +// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; +// assetCookie=0; +// } +// result.addValue(i,valueType,valueData,assetCookie,id,0); +// } +// } +// if (needStrings) { +// result.setStrings(m_strings); +// } +// } + final StringBlock getStrings() { + return m_strings; + } + + /////////////////////////////////// + private final int getAttributeOffset(int index) { + if (m_event != START_TAG) { + throw new IndexOutOfBoundsException("Current event is not START_TAG."); + } + int offset = index * 5; + if (offset >= m_attributes.length) { + throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ")."); + } + return offset; + } + + private final int findAttribute(String namespace, String attribute) { + if (m_strings == null || attribute == null) { + return -1; + } + int name = m_strings.find(attribute); + if (name == -1) { + return -1; + } + int uri = (namespace != null) + ? m_strings.find(namespace) + : -1; + for (int o = 0; o != m_attributes.length; ++o) { + if (name == m_attributes[o + ATTRIBUTE_IX_NAME] + && (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) { + return o / ATTRIBUTE_LENGHT; + } + } + return -1; + } + + private final void resetEventInfo() { + m_event = -1; + m_lineNumber = -1; + m_name = -1; + m_namespaceUri = -1; + m_attributes = null; + m_idAttribute = -1; + m_classAttribute = -1; + m_styleAttribute = -1; + } + + private final void doNext() throws IOException { + // Delayed initialization. + if (m_strings == null) { + ChunkUtil.readCheckType(m_reader, CHUNK_AXML_FILE); + /*chunkSize*/ m_reader.skipInt(); + m_strings = StringBlock.read(m_reader); + m_namespaces.increaseDepth(); + m_operational = true; + } + + if (m_event == END_DOCUMENT) { + return; + } + + int event = m_event; + resetEventInfo(); + + while (true) { + if (m_decreaseDepth) { + m_decreaseDepth = false; + m_namespaces.decreaseDepth(); + } + + // Fake END_DOCUMENT event. + if (event == END_TAG + && m_namespaces.getDepth() == 1 + && m_namespaces.getCurrentCount() == 0) { + m_event = END_DOCUMENT; + break; + } + + int chunkType; + if (event == START_DOCUMENT) { + // Fake event, see CHUNK_XML_START_TAG handler. + chunkType = CHUNK_XML_START_TAG; + } else { + chunkType = m_reader.readInt(); + } + + if (chunkType == CHUNK_RESOURCEIDS) { + int chunkSize = m_reader.readInt(); + if (chunkSize < 8 || (chunkSize % 4) != 0) { + throw new IOException("Invalid resource ids size (" + chunkSize + ")."); + } + m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); + continue; + } + + if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { + throw new IOException("Invalid chunk type (" + chunkType + ")."); + } + + // Fake START_DOCUMENT event. + if (chunkType == CHUNK_XML_START_TAG && event == -1) { + m_event = START_DOCUMENT; + break; + } + + // Common header. + /*chunkSize*/ m_reader.skipInt(); + int lineNumber = m_reader.readInt(); + /*0xFFFFFFFF*/ m_reader.skipInt(); + + if (chunkType == CHUNK_XML_START_NAMESPACE + || chunkType == CHUNK_XML_END_NAMESPACE) { + if (chunkType == CHUNK_XML_START_NAMESPACE) { + int prefix = m_reader.readInt(); + int uri = m_reader.readInt(); + m_namespaces.push(prefix, uri); + } else { + /*prefix*/ m_reader.skipInt(); + /*uri*/ m_reader.skipInt(); + m_namespaces.pop(); + } + continue; + } + + m_lineNumber = lineNumber; + + if (chunkType == CHUNK_XML_START_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); + /*flags?*/ m_reader.skipInt(); + int attributeCount = m_reader.readInt(); + m_idAttribute = (attributeCount >>> 16) - 1; + attributeCount &= 0xFFFF; + m_classAttribute = m_reader.readInt(); + m_styleAttribute = (m_classAttribute >>> 16) - 1; + m_classAttribute = (m_classAttribute & 0xFFFF) - 1; + m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT); + for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { + m_attributes[i] = (m_attributes[i] >>> 24); + i += ATTRIBUTE_LENGHT; + } + m_namespaces.increaseDepth(); + m_event = START_TAG; + break; + } + + if (chunkType == CHUNK_XML_END_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); + m_event = END_TAG; + m_decreaseDepth = true; + break; + } + + if (chunkType == CHUNK_XML_TEXT) { + m_name = m_reader.readInt(); + /*?*/ m_reader.skipInt(); + /*?*/ m_reader.skipInt(); + m_event = TEXT; + break; + } + } + } + /////////////////////////////////// data + /* + * All values are essentially indices, e.g. m_name is + * an index of name in m_strings. + */ + private IntReader m_reader; + private boolean m_operational = false; + private StringBlock m_strings; + private int[] m_resourceIDs; + private NamespaceStack m_namespaces = new NamespaceStack(); + private boolean m_decreaseDepth; + private int m_event; + private int m_lineNumber; + private int m_name; + private int m_namespaceUri; + private int[] m_attributes; + private int m_idAttribute; + private int m_classAttribute; + private int m_styleAttribute; + private static final String E_NOT_SUPPORTED = "Method is not supported."; + private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0, + ATTRIBUTE_IX_NAME = 1, + ATTRIBUTE_IX_VALUE_STRING = 2, + ATTRIBUTE_IX_VALUE_TYPE = 3, + ATTRIBUTE_IX_VALUE_DATA = 4, + ATTRIBUTE_LENGHT = 5; + private static final int CHUNK_AXML_FILE = 0x00080003, + CHUNK_RESOURCEIDS = 0x00080180, + CHUNK_XML_FIRST = 0x00100100, + CHUNK_XML_START_NAMESPACE = 0x00100100, + CHUNK_XML_END_NAMESPACE = 0x00100101, + CHUNK_XML_START_TAG = 0x00100102, + CHUNK_XML_END_TAG = 0x00100103, + CHUNK_XML_TEXT = 0x00100104, + CHUNK_XML_LAST = 0x00100104; +} diff --git a/src/brut/androlib/res/decoder/StringBlock.java b/src/brut/androlib/res/decoder/StringBlock.java index d508d932..74582c01 100644 --- a/src/brut/androlib/res/decoder/StringBlock.java +++ b/src/brut/androlib/res/decoder/StringBlock.java @@ -1,242 +1,242 @@ -/* - * Copyright 2010 Ryszard Wiśniewski . - * - * 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. - * under the License. - */ -package brut.androlib.res.decoder; - -import java.io.IOException; - -/** - * @author Ryszard Wiśniewski - * @author Dmitry Skiba - * - * Block of strings, used in binary xml and arsc. - * - * TODO: - * - implement get() - * - */ -public class StringBlock { - - /** - * Reads whole (including chunk type) string block from stream. - * Stream must be at the chunk type. - */ - public static StringBlock read(IntReader reader) throws IOException { - ChunkUtil.readCheckType(reader, CHUNK_TYPE); - int chunkSize = reader.readInt(); - int stringCount = reader.readInt(); - int styleOffsetCount = reader.readInt(); - /*?*/ reader.readInt(); - int stringsOffset = reader.readInt(); - int stylesOffset = reader.readInt(); - - StringBlock block = new StringBlock(); - block.m_stringOffsets = reader.readIntArray(stringCount); - if (styleOffsetCount != 0) { - block.m_styleOffsets = reader.readIntArray(styleOffsetCount); - } - { - int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; - if ((size % 4) != 0) { - throw new IOException("String data size is not multiple of 4 (" + size + ")."); - } - block.m_strings = reader.readIntArray(size / 4); - } - if (stylesOffset != 0) { - int size = (chunkSize - stylesOffset); - if ((size % 4) != 0) { - throw new IOException("Style data size is not multiple of 4 (" + size + ")."); - } - block.m_styles = reader.readIntArray(size / 4); - } - - return block; - } - - /** - * Returns number of strings in block. - */ - public int getCount() { - return m_stringOffsets != null - ? m_stringOffsets.length - : 0; - } - - /** - * Returns raw string (without any styling information) at specified index. - */ - public String getString(int index) { - if (index < 0 - || m_stringOffsets == null - || index >= m_stringOffsets.length) { - return null; - } - int offset = m_stringOffsets[index]; - int length = getShort(m_strings, offset); - StringBuilder result = new StringBuilder(length); - for (; length != 0; length -= 1) { - offset += 2; - result.append((char) getShort(m_strings, offset)); - } - return result.toString(); - } - - /** - * Not yet implemented. - * - * Returns string with style information (if any). - */ - public CharSequence get(int index) { - return getString(index); - } - - /** - * Returns string with style tags (html-like). - */ - public String getHTML(int index) { - String raw = getString(index); - if (raw == null) { - return raw; - } - int[] style = getStyle(index); - if (style == null) { - return raw; - } - StringBuilder html = new StringBuilder(raw.length() + 32); - int offset = 0; - while (true) { - int i = -1; - for (int j = 0; j != style.length; j += 3) { - if (style[j + 1] == -1) { - continue; - } - if (i == -1 || style[i + 1] > style[j + 1]) { - i = j; - } - } - int start = ((i != -1) ? style[i + 1] : raw.length()); - for (int j = 0; j != style.length; j += 3) { - int end = style[j + 2]; - if (end == -1 || end >= start) { - continue; - } - if (offset <= end) { - html.append(raw, offset, end + 1); - offset = end + 1; - } - style[j + 2] = -1; - html.append('<'); - html.append('/'); - html.append(getString(style[j])); - html.append('>'); - } - if (offset < start) { - html.append(raw, offset, start); - offset = start; - } - if (i == -1) { - break; - } - html.append('<'); - html.append(getString(style[i])); - html.append('>'); - style[i + 1] = -1; - } - return html.toString(); - } - - /** - * Finds index of the string. - * Returns -1 if the string was not found. - */ - public int find(String string) { - if (string == null) { - return -1; - } - for (int i = 0; i != m_stringOffsets.length; ++i) { - int offset = m_stringOffsets[i]; - int length = getShort(m_strings, offset); - if (length != string.length()) { - continue; - } - int j = 0; - for (; j != length; ++j) { - offset += 2; - if (string.charAt(j) != getShort(m_strings, offset)) { - break; - } - } - if (j == length) { - return i; - } - } - return -1; - } - - ///////////////////////////////////////////// implementation - private StringBlock() { - } - - /** - * Returns style information - array of int triplets, - * where in each triplet: - * * first int is index of tag name ('b','i', etc.) - * * second int is tag start index in string - * * third int is tag end index in string - */ - private int[] getStyle(int index) { - if (m_styleOffsets == null || m_styles == null - || index >= m_styleOffsets.length) { - return null; - } - int offset = m_styleOffsets[index] / 4; - int style[]; - { - int count = 0; - for (int i = offset; i < m_styles.length; ++i) { - if (m_styles[i] == -1) { - break; - } - count += 1; - } - if (count == 0 || (count % 3) != 0) { - return null; - } - style = new int[count]; - } - for (int i = offset, j = 0; i < m_styles.length;) { - if (m_styles[i] == -1) { - break; - } - style[j++] = m_styles[i++]; - } - return style; - } - - private static final int getShort(int[] array, int offset) { - int value = array[offset / 4]; - if ((offset % 4) / 2 == 0) { - return (value & 0xFFFF); - } else { - return (value >>> 16); - } - } - private int[] m_stringOffsets; - private int[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - private static final int CHUNK_TYPE = 0x001C0001; -} +/* + * Copyright 2010 Ryszard Wiśniewski . + * + * 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. + * under the License. + */ +package brut.androlib.res.decoder; + +import java.io.IOException; + +/** + * @author Ryszard Wiśniewski + * @author Dmitry Skiba + * + * Block of strings, used in binary xml and arsc. + * + * TODO: + * - implement get() + * + */ +public class StringBlock { + + /** + * Reads whole (including chunk type) string block from stream. + * Stream must be at the chunk type. + */ + public static StringBlock read(IntReader reader) throws IOException { + ChunkUtil.readCheckType(reader, CHUNK_TYPE); + int chunkSize = reader.readInt(); + int stringCount = reader.readInt(); + int styleOffsetCount = reader.readInt(); + /*?*/ reader.readInt(); + int stringsOffset = reader.readInt(); + int stylesOffset = reader.readInt(); + + StringBlock block = new StringBlock(); + block.m_stringOffsets = reader.readIntArray(stringCount); + if (styleOffsetCount != 0) { + block.m_styleOffsets = reader.readIntArray(styleOffsetCount); + } + { + int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; + if ((size % 4) != 0) { + throw new IOException("String data size is not multiple of 4 (" + size + ")."); + } + block.m_strings = reader.readIntArray(size / 4); + } + if (stylesOffset != 0) { + int size = (chunkSize - stylesOffset); + if ((size % 4) != 0) { + throw new IOException("Style data size is not multiple of 4 (" + size + ")."); + } + block.m_styles = reader.readIntArray(size / 4); + } + + return block; + } + + /** + * Returns number of strings in block. + */ + public int getCount() { + return m_stringOffsets != null + ? m_stringOffsets.length + : 0; + } + + /** + * Returns raw string (without any styling information) at specified index. + */ + public String getString(int index) { + if (index < 0 + || m_stringOffsets == null + || index >= m_stringOffsets.length) { + return null; + } + int offset = m_stringOffsets[index]; + int length = getShort(m_strings, offset); + StringBuilder result = new StringBuilder(length); + for (; length != 0; length -= 1) { + offset += 2; + result.append((char) getShort(m_strings, offset)); + } + return result.toString(); + } + + /** + * Not yet implemented. + * + * Returns string with style information (if any). + */ + public CharSequence get(int index) { + return getString(index); + } + + /** + * Returns string with style tags (html-like). + */ + public String getHTML(int index) { + String raw = getString(index); + if (raw == null) { + return raw; + } + int[] style = getStyle(index); + if (style == null) { + return raw; + } + StringBuilder html = new StringBuilder(raw.length() + 32); + int offset = 0; + while (true) { + int i = -1; + for (int j = 0; j != style.length; j += 3) { + if (style[j + 1] == -1) { + continue; + } + if (i == -1 || style[i + 1] > style[j + 1]) { + i = j; + } + } + int start = ((i != -1) ? style[i + 1] : raw.length()); + for (int j = 0; j != style.length; j += 3) { + int end = style[j + 2]; + if (end == -1 || end >= start) { + continue; + } + if (offset <= end) { + html.append(raw, offset, end + 1); + offset = end + 1; + } + style[j + 2] = -1; + html.append('<'); + html.append('/'); + html.append(getString(style[j])); + html.append('>'); + } + if (offset < start) { + html.append(raw, offset, start); + offset = start; + } + if (i == -1) { + break; + } + html.append('<'); + html.append(getString(style[i])); + html.append('>'); + style[i + 1] = -1; + } + return html.toString(); + } + + /** + * Finds index of the string. + * Returns -1 if the string was not found. + */ + public int find(String string) { + if (string == null) { + return -1; + } + for (int i = 0; i != m_stringOffsets.length; ++i) { + int offset = m_stringOffsets[i]; + int length = getShort(m_strings, offset); + if (length != string.length()) { + continue; + } + int j = 0; + for (; j != length; ++j) { + offset += 2; + if (string.charAt(j) != getShort(m_strings, offset)) { + break; + } + } + if (j == length) { + return i; + } + } + return -1; + } + + ///////////////////////////////////////////// implementation + private StringBlock() { + } + + /** + * Returns style information - array of int triplets, + * where in each triplet: + * * first int is index of tag name ('b','i', etc.) + * * second int is tag start index in string + * * third int is tag end index in string + */ + private int[] getStyle(int index) { + if (m_styleOffsets == null || m_styles == null + || index >= m_styleOffsets.length) { + return null; + } + int offset = m_styleOffsets[index] / 4; + int style[]; + { + int count = 0; + for (int i = offset; i < m_styles.length; ++i) { + if (m_styles[i] == -1) { + break; + } + count += 1; + } + if (count == 0 || (count % 3) != 0) { + return null; + } + style = new int[count]; + } + for (int i = offset, j = 0; i < m_styles.length;) { + if (m_styles[i] == -1) { + break; + } + style[j++] = m_styles[i++]; + } + return style; + } + + private static final int getShort(int[] array, int offset) { + int value = array[offset / 4]; + if ((offset % 4) / 2 == 0) { + return (value & 0xFFFF); + } else { + return (value >>> 16); + } + } + private int[] m_stringOffsets; + private int[] m_strings; + private int[] m_styleOffsets; + private int[] m_styles; + private static final int CHUNK_TYPE = 0x001C0001; +}