blob: 73eb066b04d14ffa5ed33754188c7fc8455f1c5f [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
Adam Lesinski1ab598f2015-08-14 14:26:04 -070017#include "compile/IdAssigner.h"
Adam Lesinskicacb28f2016-10-19 12:18:14 -070018#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#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/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -070028 * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
29 * ResourceEntry,
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070030 * as long as there is no existing ID or the ID is the same.
31 */
Adam Lesinskicacb28f2016-10-19 12:18:14 -070032static bool assignId(IDiagnostics* diag, const ResourceId& id,
33 const ResourceName& name, ResourceTablePackage* pkg,
34 ResourceTableType* type, ResourceEntry* entry) {
35 if (pkg->id.value() == id.packageId()) {
36 if (!type->id || type->id.value() == id.typeId()) {
37 type->id = id.typeId();
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070038
Adam Lesinskicacb28f2016-10-19 12:18:14 -070039 if (!entry->id || entry->id.value() == id.entryId()) {
40 entry->id = id.entryId();
41 return true;
42 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070043 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070044 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070045
Adam Lesinskicacb28f2016-10-19 12:18:14 -070046 const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0,
47 entry->id ? entry->id.value() : 0);
48 diag->error(DiagMessage() << "can't assign ID " << id << " to resource "
49 << name << " with conflicting ID " << existingId);
50 return false;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070051}
52
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070054 std::map<ResourceId, ResourceName> assignedIds;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070055
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 for (auto& package : table->packages) {
57 assert(package->id && "packages must have manually assigned IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -070058
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 for (auto& type : package->types) {
60 for (auto& entry : type->entries) {
61 const ResourceName name(package->name, type->type, entry->name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062
Adam Lesinskicacb28f2016-10-19 12:18:14 -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 =
69 assignId(context->getDiagnostics(), assignedId, name,
70 package.get(), type.get(), entry.get());
71 if (!result) {
72 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070073 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070075 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070076
Adam Lesinskicacb28f2016-10-19 12:18:14 -070077 if (package->id && type->id && entry->id) {
78 // If the ID is set for this resource, then reserve it.
79 ResourceId resourceId(package->id.value(), type->id.value(),
80 entry->id.value());
81 auto result = assignedIds.insert({resourceId, name});
82 const ResourceName& existingName = result.first->second;
83 if (!result.second) {
84 context->getDiagnostics()->error(
85 DiagMessage() << "resource " << name << " has same ID "
86 << resourceId << " as " << existingName);
87 return false;
88 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070089 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070090 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070091 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070092 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070093
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 if (mAssignedIdMap) {
95 // Reserve all the IDs mentioned in the stable ID map. That way we won't
96 // assign
97 // IDs that were listed in the map if they don't exist in the table.
98 for (const auto& stableIdEntry : *mAssignedIdMap) {
99 const ResourceName& preAssignedName = stableIdEntry.first;
100 const ResourceId& preAssignedId = stableIdEntry.second;
101 auto result = assignedIds.insert({preAssignedId, preAssignedName});
102 const ResourceName& existingName = result.first->second;
103 if (!result.second && existingName != preAssignedName) {
104 context->getDiagnostics()->error(
105 DiagMessage() << "stable ID " << preAssignedId << " for resource "
106 << preAssignedName << " is already taken by resource "
107 << existingName);
108 return false;
109 }
110 }
111 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700112
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700113 // Assign any resources without IDs the next available ID. Gaps will be filled
114 // if possible,
115 // unless those IDs have been reserved.
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700116
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117 const auto assignedIdsIterEnd = assignedIds.end();
118 for (auto& package : table->packages) {
119 assert(package->id && "packages must have manually assigned IDs");
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700120
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 // Build a half filled ResourceId object, which will be used to find the
122 // closest matching
123 // reserved ID in the assignedId map. From that point the next available
124 // type ID can be
125 // found.
126 ResourceId resourceId(package->id.value(), 0, 0);
127 uint8_t nextExpectedTypeId = 1;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700128
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700129 // Find the closest matching ResourceId that is <= the one with only the
130 // package set.
131 auto nextTypeIter = assignedIds.lower_bound(resourceId);
132 for (auto& type : package->types) {
133 if (!type->id) {
134 // We need to assign a type ID. Iterate over the reserved IDs until we
135 // find
136 // some type ID that is a distance of 2 greater than the last one we've
137 // seen.
138 // That means there is an available type ID between these reserved IDs.
139 while (nextTypeIter != assignedIdsIterEnd) {
140 if (nextTypeIter->first.packageId() != package->id.value()) {
141 break;
142 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700143
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700144 const uint8_t typeId = nextTypeIter->first.typeId();
145 if (typeId > nextExpectedTypeId) {
146 // There is a gap in the type IDs, so use the missing one.
147 type->id = nextExpectedTypeId++;
148 break;
149 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700150
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 // Set our expectation to be the next type ID after the reserved one
152 // we
153 // just saw.
154 nextExpectedTypeId = typeId + 1;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700155
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700156 // Move to the next reserved ID.
157 ++nextTypeIter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700158 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159
160 if (!type->id) {
161 // We must have hit the end of the reserved IDs and not found a gap.
162 // That means the next ID is available.
163 type->id = nextExpectedTypeId++;
164 }
165 }
166
167 resourceId = ResourceId(package->id.value(), type->id.value(), 0);
168 uint16_t nextExpectedEntryId = 0;
169
170 // Find the closest matching ResourceId that is <= the one with only the
171 // package
172 // and type set.
173 auto nextEntryIter = assignedIds.lower_bound(resourceId);
174 for (auto& entry : type->entries) {
175 if (!entry->id) {
176 // We need to assign an entry ID. Iterate over the reserved IDs until
177 // we find
178 // some entry ID that is a distance of 2 greater than the last one
179 // we've seen.
180 // That means there is an available entry ID between these reserved
181 // IDs.
182 while (nextEntryIter != assignedIdsIterEnd) {
183 if (nextEntryIter->first.packageId() != package->id.value() ||
184 nextEntryIter->first.typeId() != type->id.value()) {
185 break;
186 }
187
188 const uint16_t entryId = nextEntryIter->first.entryId();
189 if (entryId > nextExpectedEntryId) {
190 // There is a gap in the entry IDs, so use the missing one.
191 entry->id = nextExpectedEntryId++;
192 break;
193 }
194
195 // Set our expectation to be the next type ID after the reserved one
196 // we
197 // just saw.
198 nextExpectedEntryId = entryId + 1;
199
200 // Move to the next reserved entry ID.
201 ++nextEntryIter;
202 }
203
204 if (!entry->id) {
205 // We must have hit the end of the reserved IDs and not found a gap.
206 // That means the next ID is available.
207 entry->id = nextExpectedEntryId++;
208 }
209 }
210 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 }
213 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700214}
215
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700216} // namespace aapt