All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.res.android.ResXMLTree Maven / Gradle / Ivy

There is a newer version: 4.14.1
Show newest version
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