org.robolectric.res.android.ResXMLParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
package org.robolectric.res.android;
import static org.robolectric.res.android.Errors.BAD_TYPE;
import static org.robolectric.res.android.Errors.NAME_NOT_FOUND;
import static org.robolectric.res.android.Errors.NO_ERROR;
import static org.robolectric.res.android.ResTable.kDebugStringPoolNoisy;
import static org.robolectric.res.android.ResTable.kDebugXMLNoisy;
import static org.robolectric.res.android.ResXMLParser.event_code_t.BAD_DOCUMENT;
import static org.robolectric.res.android.ResXMLParser.event_code_t.END_DOCUMENT;
import static org.robolectric.res.android.ResXMLParser.event_code_t.END_NAMESPACE;
import static org.robolectric.res.android.ResXMLParser.event_code_t.END_TAG;
import static org.robolectric.res.android.ResXMLParser.event_code_t.FIRST_CHUNK_CODE;
import static org.robolectric.res.android.ResXMLParser.event_code_t.START_DOCUMENT;
import static org.robolectric.res.android.ResXMLParser.event_code_t.START_NAMESPACE;
import static org.robolectric.res.android.ResXMLParser.event_code_t.START_TAG;
import static org.robolectric.res.android.ResXMLParser.event_code_t.TEXT;
import static org.robolectric.res.android.ResourceTypes.RES_XML_CDATA_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_END_ELEMENT_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_END_NAMESPACE_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_FIRST_CHUNK_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_START_ELEMENT_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_START_NAMESPACE_TYPE;
import static org.robolectric.res.android.Util.ALOGI;
import static org.robolectric.res.android.Util.ALOGW;
import static org.robolectric.res.android.Util.dtohl;
import static org.robolectric.res.android.Util.dtohs;
import static org.robolectric.res.android.Util.isTruthy;
import org.robolectric.res.android.ResourceTypes.ResChunk_header;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_attrExt;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_attribute;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_endElementExt;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_node;
import org.robolectric.res.android.ResourceTypes.Res_value;
public class ResXMLParser {
static final int SIZEOF_RESXMLTREE_NAMESPACE_EXT = 4;
static final int SIZEOF_RESXMLTREE_NODE = ResChunk_header.SIZEOF + 8;
static final int SIZEOF_RESXMLTREE_ATTR_EXT = 20;
static final int SIZEOF_RESXMLTREE_CDATA_EXT = 4 + ResourceTypes.Res_value.SIZEOF;
static final int SIZEOF_CHAR = 2;
public static class event_code_t {
public static final int BAD_DOCUMENT = -1;
public static final int START_DOCUMENT = 0;
public static final int END_DOCUMENT = 1;
public static final int FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE;
public static final int START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE;
public static final int END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE;
public static final int START_TAG = RES_XML_START_ELEMENT_TYPE;
public static final int END_TAG = RES_XML_END_ELEMENT_TYPE;
public static final int TEXT = RES_XML_CDATA_TYPE;
}
ResXMLTree mTree;
int mEventCode;
ResXMLTree_node mCurNode;
int mCurExt;
int mSourceResourceId;
public ResXMLParser(ResXMLTree tree) {
this.mTree = tree;
this.mEventCode = BAD_DOCUMENT;
}
public void restart() {
mCurNode = null;
mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT;
}
public ResStringPool getStrings() {
return mTree.mStrings;
}
int getEventType()
{
return mEventCode;
}
public int next()
{
if (mEventCode == START_DOCUMENT) {
mCurNode = mTree.mRootNode;
mCurExt = mTree.mRootExt;
return (mEventCode=mTree.mRootCode);
} else if (mEventCode >= FIRST_CHUNK_CODE) {
return nextNode();
}
return mEventCode;
}
int getCommentID()
{
return mCurNode != null ? dtohl(mCurNode.comment.index) : -1;
}
final String getComment(Ref outLen)
{
int id = getCommentID();
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
public int getLineNumber()
{
return mCurNode != null ? dtohl(mCurNode.lineNumber) : -1;
}
public int getTextID()
{
if (mEventCode == TEXT) {
return dtohl(new ResourceTypes.ResXMLTree_cdataExt(mTree.mBuffer.buf, mCurExt).data.index);
}
return -1;
}
final String getText(Ref outLen)
{
int id = getTextID();
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
int getTextValue(Res_value outValue)
{
if (mEventCode == TEXT) {
//outValue.copyFrom_dtoh(new ResourceTypes.ResXMLTree_cdataExt(mTree.mBuffer.buf, mCurExt).typedData);
return ResourceTypes.Res_value.SIZEOF /* sizeof(Res_value) */;
}
return BAD_TYPE;
}
int getNamespacePrefixID()
{
if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
return dtohl(new ResourceTypes.ResXMLTree_namespaceExt(mTree.mBuffer.buf, mCurExt).prefix.index);
}
return -1;
}
final String getNamespacePrefix(Ref outLen)
{
int id = getNamespacePrefixID();
//printf("prefix=%d event=%s\n", id, mEventCode);
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
int getNamespaceUriID()
{
if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) {
return dtohl(new ResourceTypes.ResXMLTree_namespaceExt(mTree.mBuffer.buf, mCurExt).uri.index);
}
return -1;
}
final String getNamespaceUri(Ref outLen)
{
int id = getNamespaceUriID();
//printf("uri=%d event=%s\n", id, mEventCode);
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
public int getElementNamespaceID()
{
if (mEventCode == START_TAG) {
return dtohl(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).ns.index);
}
if (mEventCode == END_TAG) {
return dtohl(new ResXMLTree_endElementExt(mTree.mBuffer.buf, mCurExt).ns.index);
}
return -1;
}
final String getElementNamespace(Ref outLen)
{
int id = getElementNamespaceID();
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
public int getElementNameID()
{
if (mEventCode == START_TAG) {
return dtohl(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).name.index);
}
if (mEventCode == END_TAG) {
return dtohl(new ResXMLTree_endElementExt(mTree.mBuffer.buf, mCurExt).name.index);
}
return -1;
}
final String getElementName(Ref outLen)
{
int id = getElementNameID();
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
public int getAttributeCount()
{
if (mEventCode == START_TAG) {
return dtohs(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).attributeCount);
}
return 0;
}
public int getAttributeNamespaceID(int idx)
{
if (mEventCode == START_TAG) {
ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart())
// + (dtohs(tag.attributeSize())*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
return dtohl(attr.ns.index);
}
}
return -2;
}
final String getAttributeNamespace(int idx, Ref outLen)
{
int id = getAttributeNamespaceID(idx);
//printf("attribute namespace=%d idx=%d event=%s\n", id, idx, mEventCode);
if (kDebugXMLNoisy) {
System.out.println(String.format("getAttributeNamespace 0x%x=0x%x\n", idx, id));
}
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
final String getAttributeNamespace8(int idx, Ref outLen)
{
int id = getAttributeNamespaceID(idx);
//printf("attribute namespace=%d idx=%d event=%s\n", id, idx, mEventCode);
if (kDebugXMLNoisy) {
System.out.println(String.format("getAttributeNamespace 0x%x=0x%x\n", idx, id));
}
return id >= 0 ? mTree.mStrings.string8At(id, outLen) : null;
}
public int getAttributeNameID(int idx)
{
if (mEventCode == START_TAG) {
ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart())
// + (dtohs(tag.attributeSize())*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
return dtohl(attr.name.index);
}
}
return -1;
}
final String getAttributeName(int idx, Ref outLen)
{
int id = getAttributeNameID(idx);
//printf("attribute name=%d idx=%d event=%s\n", id, idx, mEventCode);
if (kDebugXMLNoisy) {
System.out.println(String.format("getAttributeName 0x%x=0x%x\n", idx, id));
}
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
final String getAttributeName8(int idx, Ref outLen)
{
int id = getAttributeNameID(idx);
//printf("attribute name=%d idx=%d event=%s\n", id, idx, mEventCode);
if (kDebugXMLNoisy) {
System.out.println(String.format("getAttributeName 0x%x=0x%x\n", idx, id));
}
return id >= 0 ? mTree.mStrings.string8At(id, outLen) : null;
}
public int getAttributeNameResID(int idx)
{
int id = getAttributeNameID(idx);
if (id >= 0 && (int)id < mTree.mNumResIds) {
int resId = dtohl(mTree.mResIds[id]);
if (mTree.mDynamicRefTable != null) {
final Ref resIdRef = new Ref<>(resId);
mTree.mDynamicRefTable.lookupResourceId(resIdRef);
resId = resIdRef.get();
}
return resId;
}
return 0;
}
public int getAttributeValueStringID(int idx)
{
if (mEventCode == START_TAG) {
ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart())
// + (dtohs(tag.attributeSize())*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
return dtohl(attr.rawValue.index);
}
}
return -1;
}
final String getAttributeStringValue(int idx, Ref outLen)
{
int id = getAttributeValueStringID(idx);
if (kDebugXMLNoisy) {
System.out.println(String.format("getAttributeValue 0x%x=0x%x\n", idx, id));
}
return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : null;
}
public int getAttributeDataType(int idx)
{
if (mEventCode == START_TAG) {
final ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart())
// + (dtohs(tag.attributeSize())*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
int type = attr.typedValue.dataType;
if (type != DataType.DYNAMIC_REFERENCE.code()) {
return type;
}
// This is a dynamic reference. We adjust those references
// to regular references at this level, so lie to the caller.
return DataType.REFERENCE.code();
}
}
return DataType.NULL.code();
}
public int getAttributeData(int idx)
{
if (mEventCode == START_TAG) {
ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart)
// + (dtohs(tag.attributeSize)*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
if (attr.typedValue.dataType != DataType.DYNAMIC_REFERENCE.code() ||
mTree.mDynamicRefTable == null) {
return dtohl(attr.typedValue.data);
}
final Ref data = new Ref<>(dtohl(attr.typedValue.data));
if (mTree.mDynamicRefTable.lookupResourceId(data) == NO_ERROR) {
return data.get();
}
}
}
return 0;
}
public int getAttributeValue(int idx, Ref outValue)
{
if (mEventCode == START_TAG) {
ResXMLTree_attrExt tag = new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt);
if (idx < dtohs(tag.attributeCount)) {
// final ResXMLTree_attribute attr = (ResXMLTree_attribute)
// (((final int8_t*)tag)
// + dtohs(tag.attributeStart())
// + (dtohs(tag.attributeSize())*idx));
ResXMLTree_attribute attr = tag.attributeAt(idx);
outValue.set(attr.typedValue);
if (mTree.mDynamicRefTable != null &&
mTree.mDynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
return BAD_TYPE;
}
return ResourceTypes.Res_value.SIZEOF /* sizeof(Res_value) */;
}
}
return BAD_TYPE;
}
int indexOfAttribute(final String ns, final String attr)
{
String nsStr = ns != null ? ns : "";
String attrStr = attr;
return indexOfAttribute(isTruthy(ns) ? nsStr : null, isTruthy(ns) ? nsStr.length() : 0,
attrStr, attrStr.length());
}
public int indexOfAttribute(final String ns, int nsLen,
final String attr, int attrLen)
{
if (mEventCode == START_TAG) {
if (attr == null) {
return NAME_NOT_FOUND;
}
final int N = getAttributeCount();
if (mTree.mStrings.isUTF8()) {
String8 ns8 = null, attr8;
if (ns != null) {
ns8 = new String8(ns, nsLen);
}
attr8 = new String8(attr, attrLen);
if (kDebugStringPoolNoisy) {
ALOGI("indexOfAttribute UTF8 %s (0x%x) / %s (0x%x)", ns8.string(), nsLen,
attr8.string(), attrLen);
}
for (int i=0; i curNsLen = new Ref<>(0), curAttrLen = new Ref<>(0);
final String curNs = getAttributeNamespace8(i, curNsLen);
final String curAttr = getAttributeName8(i, curAttrLen);
if (kDebugStringPoolNoisy) {
ALOGI(" curNs=%s (0x%x), curAttr=%s (0x%x)", curNs, curNsLen.get(), curAttr, curAttrLen.get());
}
if (curAttr != null && curNsLen.get() == nsLen && curAttrLen.get() == attrLen
&& memcmp(attr8.string(), curAttr, attrLen) == 0) {
if (ns == null) {
if (curNs == null) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
}
return i;
}
} else if (curNs != null) {
//printf(" -. ns=%s, curNs=%s\n",
// String8(ns).string(), String8(curNs).string());
if (memcmp(ns8.string(), curNs, nsLen) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
}
return i;
}
}
}
}
} else {
if (kDebugStringPoolNoisy) {
ALOGI("indexOfAttribute UTF16 %s (0x%x) / %s (0x%x)",
ns /*String8(ns, nsLen).string()*/, nsLen,
attr /*String8(attr, attrLen).string()*/, attrLen);
}
for (int i=0; i curNsLen = new Ref<>(0), curAttrLen = new Ref<>(0);
final String curNs = getAttributeNamespace(i, curNsLen);
final String curAttr = getAttributeName(i, curAttrLen);
if (kDebugStringPoolNoisy) {
ALOGI(" curNs=%s (0x%x), curAttr=%s (0x%x)",
curNs /*String8(curNs, curNsLen).string()*/, curNsLen.get(),
curAttr /*String8(curAttr, curAttrLen).string()*/, curAttrLen.get());
}
if (curAttr != null && curNsLen.get() == nsLen && curAttrLen.get() == attrLen
&& (memcmp(attr, curAttr, attrLen*SIZEOF_CHAR/*sizeof(char16_t)*/) == 0)) {
if (ns == null) {
if (curNs == null) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
}
return i;
}
} else if (curNs != null) {
//printf(" -. ns=%s, curNs=%s\n",
// String8(ns).string(), String8(curNs).string());
if (memcmp(ns, curNs, nsLen*SIZEOF_CHAR/*sizeof(char16_t)*/) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
}
return i;
}
}
}
}
}
}
return NAME_NOT_FOUND;
}
private int memcmp(String s1, String s2, int len) {
for (int i = 0; i < len; i++) {
int d = s1.charAt(i) - s2.charAt(i);
if (d != 0) {
return d;
}
}
return 0;
}
public int indexOfID()
{
if (mEventCode == START_TAG) {
final int idx = dtohs(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).idIndex);
if (idx > 0) return (idx-1);
}
return NAME_NOT_FOUND;
}
public int indexOfClass()
{
if (mEventCode == START_TAG) {
final int idx = dtohs(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).classIndex);
if (idx > 0) return (idx-1);
}
return NAME_NOT_FOUND;
}
public int indexOfStyle()
{
if (mEventCode == START_TAG) {
final int idx = dtohs(new ResXMLTree_attrExt(mTree.mBuffer.buf, mCurExt).styleIndex);
if (idx > 0) return (idx-1);
}
return NAME_NOT_FOUND;
}
int nextNode() {
if (mEventCode < 0) {
return mEventCode;
}
do {
int nextOffset = mCurNode.myOffset() + dtohl(mCurNode.header.size);
if (nextOffset >= mTree.mDataLen) {
mCurNode = null;
return (mEventCode=END_DOCUMENT);
}
// final ResXMLTree_node next = (ResXMLTree_node)
// (((final int8_t*)mCurNode) + dtohl(mCurNode.header.size));
ResXMLTree_node next = new ResXMLTree_node(mTree.mBuffer.buf, nextOffset);
if (kDebugXMLNoisy) {
ALOGI("Next node: prev=%s, next=%s\n", mCurNode, next);
}
if (next.myOffset() >= mTree.mDataLen) {
mCurNode = null;
return (mEventCode=END_DOCUMENT);
}
if (mTree.validateNode(next) != NO_ERROR) {
mCurNode = null;
return (mEventCode=BAD_DOCUMENT);
}
mCurNode = next;
final int headerSize = dtohs(next.header.headerSize);
final int totalSize = dtohl(next.header.size);
mCurExt = next.myOffset() + headerSize;
int minExtSize = 0;
int eventCode = dtohs(next.header.type);
switch ((mEventCode=eventCode)) {
case RES_XML_START_NAMESPACE_TYPE:
case RES_XML_END_NAMESPACE_TYPE:
minExtSize = SIZEOF_RESXMLTREE_NAMESPACE_EXT /*sizeof(ResXMLTree_namespaceExt)*/;
break;
case RES_XML_START_ELEMENT_TYPE:
minExtSize = SIZEOF_RESXMLTREE_ATTR_EXT /*sizeof(ResXMLTree_attrExt)*/;
break;
case RES_XML_END_ELEMENT_TYPE:
minExtSize = ResXMLTree_endElementExt.SIZEOF /*sizeof(ResXMLTree_endElementExt)*/;
break;
case RES_XML_CDATA_TYPE:
minExtSize = SIZEOF_RESXMLTREE_CDATA_EXT /*sizeof(ResXMLTree_cdataExt)*/;
break;
default:
ALOGW("Unknown XML block: header type %d in node at %d\n",
(int)dtohs(next.header.type),
(next.myOffset()-mTree.mHeader.myOffset()));
continue;
}
if ((totalSize-headerSize) < minExtSize) {
ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
(int)dtohs(next.header.type),
(next.myOffset()-mTree.mHeader.myOffset()),
(int)(totalSize-headerSize), (int)minExtSize);
return (mEventCode=BAD_DOCUMENT);
}
//printf("CurNode=%s, CurExt=%s, headerSize=%d, minExtSize=%d\n",
// mCurNode, mCurExt, headerSize, minExtSize);
return eventCode;
} while (true);
}
void getPosition(ResXMLPosition pos)
{
pos.eventCode = mEventCode;
pos.curNode = mCurNode;
pos.curExt = mCurExt;
}
void setPosition(final ResXMLPosition pos)
{
mEventCode = pos.eventCode;
mCurNode = pos.curNode;
mCurExt = pos.curExt;
}
public void setSourceResourceId(int resId) {
mSourceResourceId = resId;
}
public int getSourceResourceId() {
return mSourceResourceId;
}
static class ResXMLPosition
{
int eventCode;
ResXMLTree_node curNode;
int curExt;
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy