blob: 501ae9d08c08001102714c2b12f8978feacd02a6 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070018#include "compile/IdAssigner.h"
19#include "process/IResourceTableConsumer.h"
20#include "util/Util.h"
21
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include <cassert>
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070023#include <map>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024
25namespace aapt {
26
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070027/**
28 * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry,
29 * as long as there is no existing ID or the ID is the same.
30 */
31static bool assignId(IDiagnostics* diag, const ResourceId id, const ResourceName& name,
32 ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) {
33 if (pkg->id.value() == id.packageId()) {
34 if (!type->id || type->id.value() == id.typeId()) {
35 type->id = id.typeId();
36
37 if (!entry->id || entry->id.value() == id.entryId()) {
38 entry->id = id.entryId();
39 return true;
40 }
41 }
42 }
43
44 const ResourceId existingId(pkg->id.value(),
45 type->id ? type->id.value() : 0,
46 entry->id ? entry->id.value() : 0);
47 diag->error(DiagMessage() << "can't assign ID " << id
48 << " to resource " << name
49 << " with conflicting ID " << existingId);
50 return false;
51}
52
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070054 std::map<ResourceId, ResourceName> assignedIds;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070055
56 for (auto& package : table->packages) {
57 assert(package->id && "packages must have manually assigned IDs");
58
Adam Lesinski1ab598f2015-08-14 14:26:04 -070059 for (auto& type : package->types) {
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070060 for (auto& entry : type->entries) {
61 const ResourceName name(package->name, type->type, entry->name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070063 if (mAssignedIdMap) {
64 // Assign the pre-assigned stable ID meant for this resource.
65 const auto iter = mAssignedIdMap->find(name);
66 if (iter != mAssignedIdMap->end()) {
67 const ResourceId assignedId = iter->second;
68 const bool result = assignId(context->getDiagnostics(), assignedId, name,
69 package.get(), type.get(), entry.get());
70 if (!result) {
71 return false;
72 }
73 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070074 }
75
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070076 if (package->id && type->id && entry->id) {
77 // If the ID is set for this resource, then reserve it.
78 ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value());
79 auto result = assignedIds.insert({ resourceId, name });
80 const ResourceName& existingName = result.first->second;
81 if (!result.second) {
82 context->getDiagnostics()->error(DiagMessage() << "resource " << name
83 << " has same ID "
84 << resourceId
85 << " as " << existingName);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086 return false;
87 }
88 }
89 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070090 }
91 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070093 if (mAssignedIdMap) {
94 // Reserve all the IDs mentioned in the stable ID map. That way we won't assign
95 // IDs that were listed in the map if they don't exist in the table.
96 for (const auto& stableIdEntry : *mAssignedIdMap) {
97 const ResourceName& preAssignedName = stableIdEntry.first;
98 const ResourceId& preAssignedId = stableIdEntry.second;
99 auto result = assignedIds.insert({ preAssignedId, preAssignedName });
100 const ResourceName& existingName = result.first->second;
101 if (!result.second && existingName != preAssignedName) {
102 context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId
103 << " for resource " << preAssignedName
104 << " is already taken by resource "
105 << existingName);
106 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107 }
108 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700109 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700111 // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
112 // unless those IDs have been reserved.
113
114 const auto assignedIdsIterEnd = assignedIds.end();
115 for (auto& package : table->packages) {
116 assert(package->id && "packages must have manually assigned IDs");
117
118 // Build a half filled ResourceId object, which will be used to find the closest matching
119 // reserved ID in the assignedId map. From that point the next available type ID can be
120 // found.
121 ResourceId resourceId(package->id.value(), 0, 0);
122 uint8_t nextExpectedTypeId = 1;
123
124 // Find the closest matching ResourceId that is <= the one with only the package set.
125 auto nextTypeIter = assignedIds.lower_bound(resourceId);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700126 for (auto& type : package->types) {
127 if (!type->id) {
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700128 // We need to assign a type ID. Iterate over the reserved IDs until we find
129 // some type ID that is a distance of 2 greater than the last one we've seen.
130 // That means there is an available type ID between these reserved IDs.
131 while (nextTypeIter != assignedIdsIterEnd) {
132 if (nextTypeIter->first.packageId() != package->id.value()) {
133 break;
134 }
135
136 const uint8_t typeId = nextTypeIter->first.typeId();
137 if (typeId > nextExpectedTypeId) {
138 // There is a gap in the type IDs, so use the missing one.
139 type->id = nextExpectedTypeId++;
140 break;
141 }
142
143 // Set our expectation to be the next type ID after the reserved one we
144 // just saw.
145 nextExpectedTypeId = typeId + 1;
146
147 // Move to the next reserved ID.
148 ++nextTypeIter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700150
151 if (!type->id) {
152 // We must have hit the end of the reserved IDs and not found a gap.
153 // That means the next ID is available.
154 type->id = nextExpectedTypeId++;
155 }
156 }
157
158 resourceId = ResourceId(package->id.value(), type->id.value(), 0);
159 uint16_t nextExpectedEntryId = 0;
160
161 // Find the closest matching ResourceId that is <= the one with only the package
162 // and type set.
163 auto nextEntryIter = assignedIds.lower_bound(resourceId);
164 for (auto& entry : type->entries) {
165 if (!entry->id) {
166 // We need to assign an entry ID. Iterate over the reserved IDs until we find
167 // some entry ID that is a distance of 2 greater than the last one we've seen.
168 // That means there is an available entry ID between these reserved IDs.
169 while (nextEntryIter != assignedIdsIterEnd) {
170 if (nextEntryIter->first.packageId() != package->id.value() ||
171 nextEntryIter->first.typeId() != type->id.value()) {
172 break;
173 }
174
175 const uint16_t entryId = nextEntryIter->first.entryId();
176 if (entryId > nextExpectedEntryId) {
177 // There is a gap in the entry IDs, so use the missing one.
178 entry->id = nextExpectedEntryId++;
179 break;
180 }
181
182 // Set our expectation to be the next type ID after the reserved one we
183 // just saw.
184 nextExpectedEntryId = entryId + 1;
185
186 // Move to the next reserved entry ID.
187 ++nextEntryIter;
188 }
189
190 if (!entry->id) {
191 // We must have hit the end of the reserved IDs and not found a gap.
192 // That means the next ID is available.
193 entry->id = nextExpectedEntryId++;
194 }
195 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700196 }
197 }
198 }
199 return true;
200}
201
202} // namespace aapt