mirror of
https://github.com/revanced/Apktool.git
synced 2025-05-01 14:44:26 +02:00
908 lines
30 KiB
Java
908 lines
30 KiB
Java
/*
|
|
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
|
*
|
|
* 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 <brut.alll@gmail.com>
|
|
* @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;
|
|
}
|