org.robolectric.res.android.ResXMLTree 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.NO_ERROR;
import static org.robolectric.res.android.Errors.NO_INIT;
import static org.robolectric.res.android.ResTable.kDebugResXMLTree;
import static org.robolectric.res.android.ResTable.kDebugXMLNoisy;
import static org.robolectric.res.android.ResXMLParser.SIZEOF_RESXMLTREE_ATTR_EXT;
import static org.robolectric.res.android.ResXMLParser.SIZEOF_RESXMLTREE_NODE;
import static org.robolectric.res.android.ResXMLParser.event_code_t.BAD_DOCUMENT;
import static org.robolectric.res.android.ResXMLParser.event_code_t.START_DOCUMENT;
import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_FIRST_CHUNK_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_LAST_CHUNK_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_RESOURCE_MAP_TYPE;
import static org.robolectric.res.android.ResourceTypes.RES_XML_START_ELEMENT_TYPE;
import static org.robolectric.res.android.ResourceTypes.validate_chunk;
import static org.robolectric.res.android.Util.ALOGI;
import static org.robolectric.res.android.Util.ALOGW;
import static org.robolectric.res.android.Util.SIZEOF_INT;
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 java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicInteger;
import org.robolectric.res.android.ResourceTypes.ResChunk_header;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_attrExt;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_header;
import org.robolectric.res.android.ResourceTypes.ResXMLTree_node;
public class ResXMLTree {
final DynamicRefTable mDynamicRefTable;
public final ResXMLParser mParser;
int mError;
byte[] mOwnedData;
XmlBuffer mBuffer;
ResXMLTree_header mHeader;
int mSize;
// final uint8_t* mDataEnd;
int mDataLen;
ResStringPool mStrings = new ResStringPool();
int[] mResIds;
int mNumResIds;
ResXMLTree_node mRootNode;
int mRootExt;
int mRootCode;
static volatile AtomicInteger gCount = new AtomicInteger(0);
public ResXMLTree(DynamicRefTable dynamicRefTable) {
mParser = new ResXMLParser(this);
mDynamicRefTable = dynamicRefTable;
mError = NO_INIT;
mOwnedData = null;
if (kDebugResXMLTree) {
ALOGI("Creating ResXMLTree %s #%d\n", this, gCount.getAndIncrement()+1);
}
mParser.restart();
}
// ResXMLTree() {
// this(null);
// }
// ~ResXMLTree()
// {
@Override
protected void finalize() {
if (kDebugResXMLTree) {
ALOGI("Destroying ResXMLTree in %s #%d\n", this, gCount.getAndDecrement()-1);
}
uninit();
}
public int setTo(byte[] data, int size, boolean copyData)
{
uninit();
mParser.mEventCode = START_DOCUMENT;
if (!isTruthy(data) || !isTruthy(size)) {
return (mError=BAD_TYPE);
}
if (copyData) {
mOwnedData = new byte[size];
// if (mOwnedData == null) {
// return (mError=NO_MEMORY);
// }
// memcpy(mOwnedData, data, size);
System.arraycopy(data, 0, mOwnedData, 0, size);
data = mOwnedData;
}
mBuffer = new XmlBuffer(data);
mHeader = new ResXMLTree_header(mBuffer.buf, 0);
mSize = dtohl(mHeader.header.size);
if (dtohs(mHeader.header.headerSize) > mSize || mSize > size) {
ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n",
(int)dtohs(mHeader.header.headerSize),
(int)dtohl(mHeader.header.size), (int)size);
mError = BAD_TYPE;
mParser.restart();
return mError;
}
// mDataEnd = ((final uint8_t*)mHeader) + mSize;
mDataLen = mSize;
mStrings.uninit();
mRootNode = null;
mResIds = null;
mNumResIds = 0;
// First look for a couple interesting chunks: the string block
// and first XML node.
ResChunk_header chunk =
// (final ResChunk_header*)(((final uint8_t*)mHeader) + dtohs(mHeader.header.headerSize));
new ResChunk_header(mBuffer.buf, mHeader.header.headerSize);
ResChunk_header lastChunk = chunk;
while (chunk.myOffset() /*((final uint8_t*)chunk)*/ < (mDataLen- ResChunk_header.SIZEOF /*sizeof(ResChunk_header)*/) &&
chunk.myOffset() /*((final uint8_t*)chunk)*/ < (mDataLen-dtohl(chunk.size))) {
int err = validate_chunk(chunk, ResChunk_header.SIZEOF /*sizeof(ResChunk_header)*/, mDataLen, "XML");
if (err != NO_ERROR) {
mError = err;
// goto done;
mParser.restart();
return mError;
}
final short type = dtohs(chunk.type);
final int size1 = dtohl(chunk.size);
if (kDebugXMLNoisy) {
// System.out.println(String.format("Scanning @ %s: type=0x%x, size=0x%zx\n",
// (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size1);
}
if (type == RES_STRING_POOL_TYPE) {
mStrings.setTo(mBuffer.buf, chunk.myOffset(), size, false);
} else if (type == RES_XML_RESOURCE_MAP_TYPE) {
// mResIds = (final int*)
// (((final uint8_t*)chunk)+dtohs(chunk.headerSize()));
mNumResIds = (dtohl(chunk.size)-dtohs(chunk.headerSize))/SIZEOF_INT /*sizeof(int)*/;
mResIds = new int[mNumResIds];
for (int i = 0; i < mNumResIds; i++) {
mResIds[i] = mBuffer.buf.getInt(chunk.myOffset() + chunk.headerSize + i * SIZEOF_INT);
}
} else if (type >= RES_XML_FIRST_CHUNK_TYPE
&& type <= RES_XML_LAST_CHUNK_TYPE) {
if (validateNode(new ResXMLTree_node(mBuffer.buf, chunk)) != NO_ERROR) {
mError = BAD_TYPE;
// goto done;
mParser.restart();
return mError;
}
mParser.mCurNode = new ResXMLTree_node(mBuffer.buf, lastChunk.myOffset());
if (mParser.nextNode() == BAD_DOCUMENT) {
mError = BAD_TYPE;
// goto done;
mParser.restart();
return mError;
}
mRootNode = mParser.mCurNode;
mRootExt = mParser.mCurExt;
mRootCode = mParser.mEventCode;
break;
} else {
if (kDebugXMLNoisy) {
System.out.println("Skipping unknown chunk!\n");
}
}
lastChunk = chunk;
// chunk = (final ResChunk_header*)
// (((final uint8_t*)chunk) + size1);
chunk = new ResChunk_header(mBuffer.buf, chunk.myOffset() + size1);
}
if (mRootNode == null) {
ALOGW("Bad XML block: no root element node found\n");
mError = BAD_TYPE;
// goto done;
mParser.restart();
return mError;
}
mError = mStrings.getError();
done:
mParser.restart();
return mError;
}
public int getError()
{
return mError;
}
void uninit()
{
mError = NO_INIT;
mStrings.uninit();
if (isTruthy(mOwnedData)) {
// free(mOwnedData);
mOwnedData = null;
}
mParser.restart();
}
int validateNode(final ResXMLTree_node node)
{
final short eventCode = dtohs(node.header.type);
int err = validate_chunk(
node.header, SIZEOF_RESXMLTREE_NODE /*sizeof(ResXMLTree_node)*/,
mDataLen, "ResXMLTree_node");
if (err >= NO_ERROR) {
// Only perform additional validation on START nodes
if (eventCode != RES_XML_START_ELEMENT_TYPE) {
return NO_ERROR;
}
final short headerSize = dtohs(node.header.headerSize);
final int size = dtohl(node.header.size);
// final ResXMLTree_attrExt attrExt = (final ResXMLTree_attrExt*)
// (((final uint8_t*)node) + headerSize);
ResXMLTree_attrExt attrExt = new ResXMLTree_attrExt(mBuffer.buf, node.myOffset() + headerSize);
// check for sensical values pulled out of the stream so far...
if ((size >= headerSize + SIZEOF_RESXMLTREE_ATTR_EXT /*sizeof(ResXMLTree_attrExt)*/)
&& (attrExt.myOffset() > node.myOffset())) {
final int attrSize = ((int)dtohs(attrExt.attributeSize))
* dtohs(attrExt.attributeCount);
if ((dtohs(attrExt.attributeStart)+attrSize) <= (size-headerSize)) {
return NO_ERROR;
}
ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
(int)(dtohs(attrExt.attributeStart)+attrSize),
(int)(size-headerSize));
}
else {
ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n",
(int)headerSize, (int)size);
}
return BAD_TYPE;
}
return err;
// if (false) {
// final boolean isStart = dtohs(node.header().type()) == RES_XML_START_ELEMENT_TYPE;
//
// final short headerSize = dtohs(node.header().headerSize());
// final int size = dtohl(node.header().size());
//
// if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) {
// if (size >= headerSize) {
// if ((( final uint8_t*)node) <=(mDataEnd - size)){
// if (!isStart) {
// return NO_ERROR;
// }
// if ((((int) dtohs(node.attributeSize)) * dtohs(node.attributeCount))
// <= (size - headerSize)) {
// return NO_ERROR;
// }
// ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
// ((int) dtohs(node.attributeSize)) * dtohs(node.attributeCount),
// (int) (size - headerSize));
// return BAD_TYPE;
// }
// ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n",
// (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),(int) mSize);
// return BAD_TYPE;
// }
// ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n",
// (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),
// (int) headerSize, (int) size);
// return BAD_TYPE;
// }
// ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n",
// (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),
// (int) headerSize);
// return BAD_TYPE;
// }
}
public ResStringPool getStrings() {
return mParser.getStrings();
}
static class XmlBuffer {
final ByteBuffer buf;
public XmlBuffer(byte[] data) {
this.buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy