2015-08-14 14:26:04 -07:00
|
|
|
/*
|
|
|
|
* 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 <cassert>
|
2016-06-01 15:31:50 -07:00
|
|
|
#include <map>
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
namespace aapt {
|
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
|
2016-06-01 15:31:50 -07:00
|
|
|
std::map<ResourceId, ResourceName> assignedIds;
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
for (auto& package : table->packages) {
|
|
|
|
assert(package->id && "packages must have manually assigned IDs");
|
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
for (auto& type : package->types) {
|
|
|
|
for (auto& entry : type->entries) {
|
|
|
|
const ResourceName name(package->name, type->type, entry->name);
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
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;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2016-06-01 15:31:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
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;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2016-06-01 15:31:50 -07:00
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
// 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;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2016-06-01 15:31:50 -07:00
|
|
|
|
|
|
|
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++;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
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);
|
2015-08-14 14:26:04 -07:00
|
|
|
for (auto& entry : type->entries) {
|
|
|
|
if (!entry->id) {
|
2016-06-01 15:31:50 -07:00
|
|
|
// 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;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2016-06-01 15:31:50 -07:00
|
|
|
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++;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace aapt
|