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

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

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 - 2025 Weber Informatics LLC | Privacy Policy