blob: 17c22c5aac6b7bb5e3d2c75e7de6ef89791c47e0 [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 Lesinskice5e56e2016-10-21 17:56:45 -070018
19#include <map>
20
21#include "android-base/logging.h"
22
Adam Lesinskicacb28f2016-10-19 12:18:14 -070023#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "process/IResourceTableConsumer.h"
25#include "util/Util.h"
26
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027namespace aapt {
28
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070029/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -070030 * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
31 * ResourceEntry,
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070032 * as long as there is no existing ID or the ID is the same.
33 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070034static bool AssignId(IDiagnostics* diag, const ResourceId& id,
Adam Lesinskicacb28f2016-10-19 12:18:14 -070035 const ResourceName& name, ResourceTablePackage* pkg,
36 ResourceTableType* type, ResourceEntry* entry) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070037 if (pkg->id.value() == id.package_id()) {
38 if (!type->id || type->id.value() == id.type_id()) {
39 type->id = id.type_id();
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070040
Adam Lesinskice5e56e2016-10-21 17:56:45 -070041 if (!entry->id || entry->id.value() == id.entry_id()) {
42 entry->id = id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -070043 return true;
44 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070045 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070046 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070047
Adam Lesinskice5e56e2016-10-21 17:56:45 -070048 const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
49 entry->id ? entry->id.value() : 0);
50 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
51 << name << " with conflicting ID " << existing_id);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 return false;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070053}
54
Adam Lesinskice5e56e2016-10-21 17:56:45 -070055bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
56 std::map<ResourceId, ResourceName> assigned_ids;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070057
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 for (auto& package : table->packages) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070059 CHECK(bool(package->id)) << "packages must have manually assigned IDs";
Adam Lesinski1ab598f2015-08-14 14:26:04 -070060
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 for (auto& type : package->types) {
62 for (auto& entry : type->entries) {
63 const ResourceName name(package->name, type->type, entry->name);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070064
Adam Lesinskice5e56e2016-10-21 17:56:45 -070065 if (assigned_id_map_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070066 // Assign the pre-assigned stable ID meant for this resource.
Adam Lesinskice5e56e2016-10-21 17:56:45 -070067 const auto iter = assigned_id_map_->find(name);
68 if (iter != assigned_id_map_->end()) {
69 const ResourceId assigned_id = iter->second;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070070 const bool result =
Adam Lesinskice5e56e2016-10-21 17:56:45 -070071 AssignId(context->GetDiagnostics(), assigned_id, name,
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072 package.get(), type.get(), entry.get());
73 if (!result) {
74 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070075 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070076 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070077 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070078
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 if (package->id && type->id && entry->id) {
80 // If the ID is set for this resource, then reserve it.
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 ResourceId resource_id(package->id.value(), type->id.value(),
82 entry->id.value());
83 auto result = assigned_ids.insert({resource_id, name});
84 const ResourceName& existing_name = result.first->second;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070085 if (!result.second) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070086 context->GetDiagnostics()->Error(
Adam Lesinskicacb28f2016-10-19 12:18:14 -070087 DiagMessage() << "resource " << name << " has same ID "
Adam Lesinskice5e56e2016-10-21 17:56:45 -070088 << resource_id << " as " << existing_name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070089 return false;
90 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070091 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070092 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070093 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070095
Adam Lesinskice5e56e2016-10-21 17:56:45 -070096 if (assigned_id_map_) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070097 // Reserve all the IDs mentioned in the stable ID map. That way we won't
98 // assign
99 // IDs that were listed in the map if they don't exist in the table.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700100 for (const auto& stable_id_entry : *assigned_id_map_) {
101 const ResourceName& pre_assigned_name = stable_id_entry.first;
102 const ResourceId& pre_assigned_id = stable_id_entry.second;
103 auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
104 const ResourceName& existing_name = result.first->second;
105 if (!result.second && existing_name != pre_assigned_name) {
106 context->GetDiagnostics()->Error(
107 DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
108 << pre_assigned_name
109 << " is already taken by resource " << existing_name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700110 return false;
111 }
112 }
113 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700114
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 // Assign any resources without IDs the next available ID. Gaps will be filled
116 // if possible,
117 // unless those IDs have been reserved.
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700118
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700119 const auto assigned_ids_iter_end = assigned_ids.end();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 for (auto& package : table->packages) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700121 CHECK(bool(package->id)) << "packages must have manually assigned IDs";
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700122
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700123 // Build a half filled ResourceId object, which will be used to find the
124 // closest matching
125 // reserved ID in the assignedId map. From that point the next available
126 // type ID can be
127 // found.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700128 ResourceId resource_id(package->id.value(), 0, 0);
129 uint8_t next_expected_type_id = 1;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700130
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700131 // Find the closest matching ResourceId that is <= the one with only the
132 // package set.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 auto next_type_iter = assigned_ids.lower_bound(resource_id);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700134 for (auto& type : package->types) {
135 if (!type->id) {
136 // We need to assign a type ID. Iterate over the reserved IDs until we
137 // find
138 // some type ID that is a distance of 2 greater than the last one we've
139 // seen.
140 // That means there is an available type ID between these reserved IDs.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700141 while (next_type_iter != assigned_ids_iter_end) {
142 if (next_type_iter->first.package_id() != package->id.value()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 break;
144 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700145
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700146 const uint8_t type_id = next_type_iter->first.type_id();
147 if (type_id > next_expected_type_id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 // There is a gap in the type IDs, so use the missing one.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700149 type->id = next_expected_type_id++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 break;
151 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700152
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700153 // Set our expectation to be the next type ID after the reserved one
154 // we
155 // just saw.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700156 next_expected_type_id = type_id + 1;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700157
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700158 // Move to the next reserved ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700159 ++next_type_iter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700160 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161
162 if (!type->id) {
163 // We must have hit the end of the reserved IDs and not found a gap.
164 // That means the next ID is available.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700165 type->id = next_expected_type_id++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 }
167 }
168
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700169 resource_id = ResourceId(package->id.value(), type->id.value(), 0);
170 uint16_t next_expected_entry_id = 0;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700171
172 // Find the closest matching ResourceId that is <= the one with only the
173 // package
174 // and type set.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700175 auto next_entry_iter = assigned_ids.lower_bound(resource_id);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700176 for (auto& entry : type->entries) {
177 if (!entry->id) {
178 // We need to assign an entry ID. Iterate over the reserved IDs until
179 // we find
180 // some entry ID that is a distance of 2 greater than the last one
181 // we've seen.
182 // That means there is an available entry ID between these reserved
183 // IDs.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700184 while (next_entry_iter != assigned_ids_iter_end) {
185 if (next_entry_iter->first.package_id() != package->id.value() ||
186 next_entry_iter->first.type_id() != type->id.value()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700187 break;
188 }
189
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700190 const uint16_t entry_id = next_entry_iter->first.entry_id();
191 if (entry_id > next_expected_entry_id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700192 // There is a gap in the entry IDs, so use the missing one.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700193 entry->id = next_expected_entry_id++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 break;
195 }
196
197 // Set our expectation to be the next type ID after the reserved one
198 // we
199 // just saw.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700200 next_expected_entry_id = entry_id + 1;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700201
202 // Move to the next reserved entry ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700203 ++next_entry_iter;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700204 }
205
206 if (!entry->id) {
207 // We must have hit the end of the reserved IDs and not found a gap.
208 // That means the next ID is available.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700209 entry->id = next_expected_entry_id++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 }
211 }
212 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700213 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 }
215 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216}
217
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700218} // namespace aapt