/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ResourceTable.h" #include "compile/IdAssigner.h" #include "process/IResourceTableConsumer.h" #include "util/Util.h" #include #include namespace aapt { /** * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry, * as long as there is no existing ID or the ID is the same. */ static bool assignId(IDiagnostics* diag, const ResourceId id, const ResourceName& name, ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) { if (pkg->id.value() == id.packageId()) { if (!type->id || type->id.value() == id.typeId()) { type->id = id.typeId(); if (!entry->id || entry->id.value() == id.entryId()) { entry->id = id.entryId(); return true; } } } const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0, entry->id ? entry->id.value() : 0); diag->error(DiagMessage() << "can't assign ID " << id << " to resource " << name << " with conflicting ID " << existingId); return false; } bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) { std::map assignedIds; for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); for (auto& type : package->types) { for (auto& entry : type->entries) { const ResourceName name(package->name, type->type, entry->name); if (mAssignedIdMap) { // Assign the pre-assigned stable ID meant for this resource. const auto iter = mAssignedIdMap->find(name); if (iter != mAssignedIdMap->end()) { const ResourceId assignedId = iter->second; const bool result = assignId(context->getDiagnostics(), assignedId, name, package.get(), type.get(), entry.get()); if (!result) { return false; } } } if (package->id && type->id && entry->id) { // If the ID is set for this resource, then reserve it. ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value()); auto result = assignedIds.insert({ resourceId, name }); const ResourceName& existingName = result.first->second; if (!result.second) { context->getDiagnostics()->error(DiagMessage() << "resource " << name << " has same ID " << resourceId << " as " << existingName); return false; } } } } } if (mAssignedIdMap) { // Reserve all the IDs mentioned in the stable ID map. That way we won't assign // IDs that were listed in the map if they don't exist in the table. for (const auto& stableIdEntry : *mAssignedIdMap) { const ResourceName& preAssignedName = stableIdEntry.first; const ResourceId& preAssignedId = stableIdEntry.second; auto result = assignedIds.insert({ preAssignedId, preAssignedName }); const ResourceName& existingName = result.first->second; if (!result.second && existingName != preAssignedName) { context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId << " for resource " << preAssignedName << " is already taken by resource " << existingName); return false; } } } // Assign any resources without IDs the next available ID. Gaps will be filled if possible, // unless those IDs have been reserved. const auto assignedIdsIterEnd = assignedIds.end(); for (auto& package : table->packages) { assert(package->id && "packages must have manually assigned IDs"); // Build a half filled ResourceId object, which will be used to find the closest matching // reserved ID in the assignedId map. From that point the next available type ID can be // found. ResourceId resourceId(package->id.value(), 0, 0); uint8_t nextExpectedTypeId = 1; // Find the closest matching ResourceId that is <= the one with only the package set. auto nextTypeIter = assignedIds.lower_bound(resourceId); for (auto& type : package->types) { if (!type->id) { // We need to assign a type ID. Iterate over the reserved IDs until we find // some type ID that is a distance of 2 greater than the last one we've seen. // That means there is an available type ID between these reserved IDs. while (nextTypeIter != assignedIdsIterEnd) { if (nextTypeIter->first.packageId() != package->id.value()) { break; } const uint8_t typeId = nextTypeIter->first.typeId(); if (typeId > nextExpectedTypeId) { // There is a gap in the type IDs, so use the missing one. type->id = nextExpectedTypeId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedTypeId = typeId + 1; // Move to the next reserved ID. ++nextTypeIter; } if (!type->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. type->id = nextExpectedTypeId++; } } resourceId = ResourceId(package->id.value(), type->id.value(), 0); uint16_t nextExpectedEntryId = 0; // Find the closest matching ResourceId that is <= the one with only the package // and type set. auto nextEntryIter = assignedIds.lower_bound(resourceId); for (auto& entry : type->entries) { if (!entry->id) { // We need to assign an entry ID. Iterate over the reserved IDs until we find // some entry ID that is a distance of 2 greater than the last one we've seen. // That means there is an available entry ID between these reserved IDs. while (nextEntryIter != assignedIdsIterEnd) { if (nextEntryIter->first.packageId() != package->id.value() || nextEntryIter->first.typeId() != type->id.value()) { break; } const uint16_t entryId = nextEntryIter->first.entryId(); if (entryId > nextExpectedEntryId) { // There is a gap in the entry IDs, so use the missing one. entry->id = nextExpectedEntryId++; break; } // Set our expectation to be the next type ID after the reserved one we // just saw. nextExpectedEntryId = entryId + 1; // Move to the next reserved entry ID. ++nextEntryIter; } if (!entry->id) { // We must have hit the end of the reserved IDs and not found a gap. // That means the next ID is available. entry->id = nextExpectedEntryId++; } } } } } return true; } } // namespace aapt