private int getEntry()

in resources/src/main/java/org/robolectric/res/android/ResTable.java [518:719]


  private int getEntry(
      final PackageGroup packageGroup, int typeIndex, int entryIndex,
      final ResTable_config config,
      Entry outEntry)
  {
    final List<Type> typeList = getOrDefault(packageGroup.types, typeIndex, Collections.emptyList());
    if (typeList.isEmpty()) {
      ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
      return BAD_TYPE;
    }

    ResTable_type bestType = null;
    int bestOffset = ResTable_type.NO_ENTRY;
    Package bestPackage = null;
    int specFlags = 0;
    byte actualTypeIndex = (byte) typeIndex;
    ResTable_config bestConfig = null;
//    memset(&bestConfig, 0, sizeof(bestConfig));

    // Iterate over the Types of each package.
    final int typeCount = typeList.size();
    for (int i = 0; i < typeCount; i++) {
      final Type typeSpec = typeList.get(i);

      int realEntryIndex = entryIndex;
      int realTypeIndex = typeIndex;
      boolean currentTypeIsOverlay = false;

      // Runtime overlay packages provide a mapping of app resource
      // ID to package resource ID.
      if (typeSpec.idmapEntries.hasEntries()) {
        final Ref<Short> overlayEntryIndex = new Ref<>((short) 0);
        if (typeSpec.idmapEntries.lookup(entryIndex, overlayEntryIndex) != NO_ERROR) {
          // No such mapping exists
          continue;
        }
        realEntryIndex = overlayEntryIndex.get();
        realTypeIndex = typeSpec.idmapEntries.overlayTypeId() - 1;
        currentTypeIsOverlay = true;
      }

      // Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
      // Particular types (ResTable_type) may be encoded with sparse entries, and so their
      // entryCount do not need to match.
      if (((int) realEntryIndex) >= typeSpec.entryCount) {
        ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
            Res_MAKEID(packageGroup.id - 1, typeIndex, entryIndex),
            entryIndex, ((int) typeSpec.entryCount));
        // We should normally abort here, but some legacy apps declare
        // resources in the 'android' package (old bug in AAPT).
        continue;
      }

      // Aggregate all the flags for each package that defines this entry.
      if (typeSpec.typeSpecFlags != null) {
        specFlags |= dtohl(typeSpec.typeSpecFlags[realEntryIndex]);
      } else {
        specFlags = -1;
      }

      List<ResTable_type> candidateConfigs = typeSpec.configs;

//      List<ResTable_type> filteredConfigs;
//      if (isTruthy(config) && Objects.equals(mParams, config)) {
//        // Grab the lock first so we can safely get the current filtered list.
//        synchronized (mFilteredConfigLock) {
//          // This configuration is equal to the one we have previously cached for,
//          // so use the filtered configs.
//
//          final TypeCacheEntry cacheEntry = packageGroup.typeCacheEntries.get(typeIndex);
//          if (i < cacheEntry.filteredConfigs.size()) {
//            if (isTruthy(cacheEntry.filteredConfigs.get(i))) {
//              // Grab a reference to the shared_ptr so it doesn't get destroyed while
//              // going through this list.
//              filteredConfigs = cacheEntry.filteredConfigs.get(i);
//
//              // Use this filtered list.
//              candidateConfigs = filteredConfigs;
//            }
//          }
//        }
//      }

      final int numConfigs = candidateConfigs.size();
      for (int c = 0; c < numConfigs; c++) {
        final ResTable_type thisType = candidateConfigs.get(c);
        if (thisType == NULL) {
          continue;
        }

        final ResTable_config thisConfig;
//        thisConfig.copyFromDtoH(thisType.config);
        thisConfig = ResTable_config.fromDtoH(thisType.config);

        // Check to make sure this one is valid for the current parameters.
        if (config != NULL && !thisConfig.match(config)) {
          continue;
        }

        // const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
        // reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));

        final int eindex = thisType.myOffset() + dtohs(thisType.header.headerSize);

        int thisOffset;

        // Check if there is the desired entry in this type.
        if (isTruthy(thisType.flags & ResTable_type.FLAG_SPARSE)) {
          // This is encoded as a sparse map, so perform a binary search.
          final ByteBuffer buf = thisType.myBuf();
          ResTable_sparseTypeEntry sparseIndices = new ResTable_sparseTypeEntry(buf, eindex);
          ResTable_sparseTypeEntry result = lower_bound(
              sparseIndices,
              new ResTable_sparseTypeEntry(buf, sparseIndices.myOffset() + dtohl(thisType.entryCount)),
              new ResTable_sparseTypeEntry(buf, realEntryIndex),
              (a, b) -> dtohs(a.idxOrOffset) < dtohs(b.idxOrOffset));
//          if (result == sparseIndices + dtohl(thisType.entryCount)
//              || dtohs(result.idx) != realEntryIndex) {
          if (result.myOffset() == sparseIndices.myOffset() + dtohl(thisType.entryCount)
              || dtohs(result.idxOrOffset) != realEntryIndex) {
            // No entry found.
            continue;
          }
          // Extract the offset from the entry. Each offset must be a multiple of 4
          // so we store it as the real offset divided by 4.
//          thisOffset = dtohs(result->offset) * 4u;
          thisOffset = dtohs(result.idxOrOffset) * 4;
        } else {
          if (realEntryIndex >= dtohl(thisType.entryCount)) {
            // Entry does not exist.
            continue;
          }
//          thisOffset = dtohl(eindex[realEntryIndex]);
          thisOffset = thisType.entryOffset(realEntryIndex);
        }

        if (thisOffset == ResTable_type.NO_ENTRY) {
          // There is no entry for this index and configuration.
          continue;
        }

        if (bestType != NULL) {
          // Check if this one is less specific than the last found.  If so,
          // we will skip it.  We check starting with things we most care
          // about to those we least care about.
          if (!thisConfig.isBetterThan(bestConfig, config)) {
            if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
              continue;
            }
          }
        }

        bestType = thisType;
        bestOffset = thisOffset;
        bestConfig = thisConfig;
        bestPackage = typeSpec._package_;
        actualTypeIndex = (byte) realTypeIndex;

        // If no config was specified, any type will do, so skip
        if (config == NULL) {
          break;
        }
      }
    }

    if (bestType == NULL) {
      return BAD_INDEX;
    }

    bestOffset += dtohl(bestType.entriesStart);

//    if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
    if (bestOffset > (dtohl(bestType.header.size)- ResTable_entry.SIZEOF)) {
      ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
          bestOffset, dtohl(bestType.header.size));
      return BAD_TYPE;
    }
    if ((bestOffset & 0x3) != 0) {
      ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
      return BAD_TYPE;
    }

//    const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
//      reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
    final ResTable_entry entry = new ResTable_entry(bestType.myBuf(),
        bestType.myOffset() + bestOffset);
    if (dtohs(entry.size) < ResTable_entry.SIZEOF) {
      ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry.size));
      return BAD_TYPE;
    }
    
    if (outEntry != null) {
      outEntry.entry = entry;
      outEntry.config = bestConfig;
      outEntry.type = bestType;
      outEntry.specFlags = specFlags;
      outEntry._package_ = bestPackage;
      outEntry.typeStr = new StringPoolRef(bestPackage.typeStrings, actualTypeIndex - bestPackage.typeIdOffset);
      outEntry.keyStr = new StringPoolRef(bestPackage.keyStrings, dtohl(entry.key.index));
    }
    return NO_ERROR;
  }