/* * Copyright (c) 2009-2013 Panxiaobo * * 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 pxb.android.axml; import pxb.android.ResConst; import pxb.android.StringItems; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import static pxb.android.axml.NodeVisitor.TYPE_INT_BOOLEAN; import static pxb.android.axml.NodeVisitor.TYPE_STRING; /** * a class to read android axml * * @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a> */ public class AxmlParser implements ResConst { public static final int END_FILE = 7; public static final int END_NS = 5; public static final int END_TAG = 3; public static final int START_FILE = 1; public static final int START_NS = 4; public static final int START_TAG = 2; public static final int TEXT = 6; // private int attrName[]; // private int attrNs[]; // private int attrResId[]; // private int attrType[]; // private Object attrValue[]; private int attributeCount; private IntBuffer attrs; private int classAttribute; private int fileSize = -1; private int idAttribute; private ByteBuffer in; private int lineNumber; private int nameIdx; private int nsIdx; private int prefixIdx; private int[] resourceIds; private String[] strings; private int styleAttribute; private int textIdx; public AxmlParser(byte[] data) { this(ByteBuffer.wrap(data)); } public AxmlParser(ByteBuffer in) { super(); this.in = in.order(ByteOrder.LITTLE_ENDIAN); } public int getAttrCount() { return attributeCount; } public int getAttributeCount() { return attributeCount; } public String getAttrName(int i) { int idx = attrs.get(i * 5 + 1); return strings[idx]; } public String getAttrNs(int i) { int idx = attrs.get(i * 5 + 0); return idx >= 0 ? strings[idx] : null; } String getAttrRawString(int i) { int idx = attrs.get(i * 5 + 2); if (idx >= 0) { return strings[idx]; } return null; } public int getAttrResId(int i) { if (resourceIds != null) { int idx = attrs.get(i * 5 + 1); if (idx >= 0 && idx < resourceIds.length) { return resourceIds[idx]; } } return -1; } public int getAttrType(int i) { return attrs.get(i * 5 + 3) >> 24; } public Object getAttrValue(int i) { int v = attrs.get(i * 5 + 4); if (i == idAttribute) { return ValueWrapper.wrapId(v, getAttrRawString(i)); } else if (i == styleAttribute) { return ValueWrapper.wrapStyle(v, getAttrRawString(i)); } else if (i == classAttribute) { return ValueWrapper.wrapClass(v, getAttrRawString(i)); } switch (getAttrType(i)) { case TYPE_STRING: return strings[v]; case TYPE_INT_BOOLEAN: return v != 0; default: return v; } } public int getLineNumber() { return lineNumber; } public String getName() { return strings[nameIdx]; } public String getNamespacePrefix() { return strings[prefixIdx]; } public String getNamespaceUri() { return nsIdx >= 0 ? strings[nsIdx] : null; } public String getText() { return strings[textIdx]; } public int next() throws IOException { if (fileSize < 0) { int type = in.getInt() & 0xFFFF; if (type != RES_XML_TYPE) { throw new RuntimeException(); } fileSize = in.getInt(); return START_FILE; } int event = -1; for (int p = in.position(); p < fileSize; p = in.position()) { int type = in.getInt() & 0xFFFF; int size = in.getInt(); switch (type) { case RES_XML_START_ELEMENT_TYPE: { { lineNumber = in.getInt(); in.getInt();/* skip, 0xFFFFFFFF */ nsIdx = in.getInt(); nameIdx = in.getInt(); int flag = in.getInt();// 0x00140014 ? if (flag != 0x00140014) { throw new RuntimeException(); } } attributeCount = in.getShort() & 0xFFFF; idAttribute = (in.getShort() & 0xFFFF) - 1; classAttribute = (in.getShort() & 0xFFFF) - 1; styleAttribute = (in.getShort() & 0xFFFF) - 1; attrs = in.asIntBuffer(); // attrResId = new int[attributeCount]; // attrName = new int[attributeCount]; // attrNs = new int[attributeCount]; // attrType = new int[attributeCount]; // attrValue = new Object[attributeCount]; // for (int i = 0; i < attributeCount; i++) { // int attrNsIdx = in.getInt(); // int attrNameIdx = in.getInt(); // int raw = in.getInt(); // int aValueType = in.getInt() >>> 24; // int aValue = in.getInt(); // Object value = null; // switch (aValueType) { // case TYPE_STRING: // value = strings[aValue]; // break; // case TYPE_INT_BOOLEAN: // value = aValue != 0; // break; // default: // value = aValue; // } // int resourceId = attrNameIdx < this.resourceIds.length ? // resourceIds[attrNameIdx] : -1; // attrNs[i] = attrNsIdx; // attrName[i] = attrNameIdx; // attrType[i] = aValueType; // attrResId[i] = resourceId; // attrValue[i] = value; // } event = START_TAG; } break; case RES_XML_END_ELEMENT_TYPE: { in.position(p + size); event = END_TAG; } break; case RES_XML_START_NAMESPACE_TYPE: lineNumber = in.getInt(); in.getInt();/* 0xFFFFFFFF */ prefixIdx = in.getInt(); nsIdx = in.getInt(); event = START_NS; break; case RES_XML_END_NAMESPACE_TYPE: in.position(p + size); event = END_NS; break; case RES_STRING_POOL_TYPE: strings = StringItems.read(in); in.position(p + size); continue; case RES_XML_RESOURCE_MAP_TYPE: int count = size / 4 - 2; resourceIds = new int[count]; for (int i = 0; i < count; i++) { resourceIds[i] = in.getInt(); } in.position(p + size); continue; case RES_XML_CDATA_TYPE: lineNumber = in.getInt(); in.getInt();/* 0xFFFFFFFF */ textIdx = in.getInt(); in.getInt();/* 00000008 00000000 */ in.getInt(); event = TEXT; break; default: throw new RuntimeException(); } in.position(p + size); return event; } return END_FILE; } }