blob: e0a9a31eee8be9798cc17ef8c1108af86a2cd872 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 Lesinskicacb28f2016-10-19 12:18:14 -070017#include "ResourceTable.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinskicacb28f2016-10-19 12:18:14 -070019#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <memory>
21#include <string>
22#include <tuple>
23
Adam Lesinski66ea8402017-06-28 11:44:11 -070024#include "android-base/logging.h"
Adam Lesinski71be7052017-12-12 16:48:07 -080025#include "android-base/stringprintf.h"
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020026#include "androidfw/ConfigDescription.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070027#include "androidfw/ResourceTypes.h"
28
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070029#include "Debug.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070030#include "NameMangler.h"
31#include "ResourceValues.h"
32#include "ValueVisitor.h"
Fabien Sanglard2d34e762019-02-21 15:13:29 -080033#include "trace/TraceBuffer.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070034#include "text/Unicode.h"
35#include "util/Util.h"
36
37using ::aapt::text::IsValidResourceEntryName;
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020038using ::android::ConfigDescription;
Adam Lesinski66ea8402017-06-28 11:44:11 -070039using ::android::StringPiece;
Adam Lesinski71be7052017-12-12 16:48:07 -080040using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080041
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080042namespace aapt {
43
Ryan Mitchell54237ff2018-12-13 15:44:29 -080044const char* Overlayable::kActorScheme = "overlay";
45
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070046static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
47 const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
48 return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049}
50
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070052static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054}
55
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000056template <typename T>
57static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -070058 const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000059 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070060 return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000061}
62
Adam Lesinski71be7052017-12-12 16:48:07 -080063ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070064 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070065 auto iter = std::lower_bound(packages.begin(), last, name,
66 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070067 if (iter != last && name == (*iter)->name) {
68 return iter->get();
69 }
70 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071}
72
Adam Lesinski71be7052017-12-12 16:48:07 -080073ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 for (auto& package : packages) {
75 if (package->id && package->id.value() == id) {
76 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070078 }
79 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080080}
81
Adam Lesinskib1afa072017-03-29 13:52:38 -070082ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Fabien Sanglard2d34e762019-02-21 15:13:29 -080083 TRACE_CALL();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070084 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070085 if (id && !package->id) {
86 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070087 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 }
89
90 if (id && package->id && package->id.value() != id.value()) {
91 return nullptr;
92 }
93 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080094}
95
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000096ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
97 const Maybe<uint8_t> id) {
98 const auto last = packages.end();
99 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
100 less_than_struct_with_name_and_id<ResourceTablePackage>);
101
102 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
103 return iter->get();
104 }
105
106 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
107 new_package->name = name.to_string();
108 new_package->id = id;
109 return packages.emplace(iter, std::move(new_package))->get();
110}
111
Adam Lesinskib1afa072017-03-29 13:52:38 -0700112ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700113 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700114 auto iter = std::lower_bound(packages.begin(), last, name,
115 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116 if (iter != last && name == (*iter)->name) {
117 return iter->get();
118 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800119
Adam Lesinskib1afa072017-03-29 13:52:38 -0700120 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800121 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700123}
124
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700125ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700127 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
128 less_than_type_and_id);
129 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 return iter->get();
131 }
132 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133}
134
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700135ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
136 const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700137 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700138 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
139 less_than_type_and_id);
140 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 return iter->get();
142 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700143
144 auto new_type = new ResourceTableType(type);
145 new_type->id = id;
146 return types.emplace(iter, std::move(new_type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147}
148
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700149ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 const auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700151 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
152 less_than_struct_with_name_and_id<ResourceEntry>);
153 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 return iter->get();
155 }
156 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700157}
158
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700159ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700160 const Maybe<uint16_t > id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700162 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
163 less_than_struct_with_name_and_id<ResourceEntry>);
164 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 return iter->get();
166 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700167
168 auto new_entry = new ResourceEntry(name);
169 new_entry->id = id;
170 return entries.emplace(iter, std::move(new_entry))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700171}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800172
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
174 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800175}
176
177struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178 const ConfigDescription* config;
179 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800180};
181
Adam Lesinski34a16872018-02-23 16:18:10 -0800182bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700183 int cmp = lhs->config.compare(*rhs.config);
184 if (cmp == 0) {
185 cmp = StringPiece(lhs->product).compare(rhs.product);
186 }
187 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800188}
189
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700190ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800191 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800192 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
193 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 if (iter != values.end()) {
195 ResourceConfigValue* value = iter->get();
196 if (value->config == config && StringPiece(value->product) == product) {
197 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800198 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 }
200 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800201}
202
Adam Lesinskib1afa072017-03-29 13:52:38 -0700203ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
204 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800205 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
206 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 if (iter != values.end()) {
208 ResourceConfigValue* value = iter->get();
209 if (value->config == config && StringPiece(value->product) == product) {
210 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800211 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 }
213 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700214 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700215 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800216}
217
Adam Lesinskib1afa072017-03-29 13:52:38 -0700218std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700219 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800220
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700221 auto iter = values.begin();
222 for (; iter != values.end(); ++iter) {
223 ResourceConfigValue* value = iter->get();
224 if (value->config == config) {
225 results.push_back(value);
226 ++iter;
227 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800228 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700229 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800230
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700231 for (; iter != values.end(); ++iter) {
232 ResourceConfigValue* value = iter->get();
233 if (value->config == config) {
234 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800235 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 }
237 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800238}
239
Adam Lesinski34a16872018-02-23 16:18:10 -0800240bool ResourceEntry::HasDefaultValue() const {
241 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
242
243 // The default config should be at the top of the list, since the list is sorted.
244 for (auto& config_value : values) {
245 if (config_value->config == default_config) {
246 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700247 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800249 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700250}
251
Adam Lesinski71be7052017-12-12 16:48:07 -0800252// The default handler for collisions.
253//
254// Typically, a weak value will be overridden by a strong value. An existing weak
255// value will not be overridden by an incoming weak value.
256//
257// There are some exceptions:
258//
259// Attributes: There are two types of Attribute values: USE and DECL.
260//
261// USE is anywhere an Attribute is declared without a format, and in a place that would
262// be legal to declare if the Attribute already existed. This is typically in a
263// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
264//
265// DECL is an absolute declaration of an Attribute and specifies an explicit format.
266//
267// A DECL will override a USE without error. Two DECLs must match in their format for there to be
268// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700269ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
270 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700271 Attribute* existing_attr = ValueCast<Attribute>(existing);
272 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
273 if (!incoming_attr) {
274 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700275 // We're trying to add a weak resource but a resource
276 // already exists. Keep the existing.
277 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700279 // Override the weak resource with the new strong resource.
280 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800281 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700282 // The existing and incoming values are strong, this is an error
283 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700284 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700285 }
286
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700287 if (!existing_attr) {
288 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700289 // The existing value is not an attribute and it is weak,
290 // so take the incoming attribute value.
291 return CollisionResult::kTakeNew;
292 }
293 // The existing value is not an attribute and it is strong,
294 // so the incoming attribute value is an error.
295 return CollisionResult::kConflict;
296 }
297
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700299
300 //
301 // Attribute specific handling. At this point we know both
302 // values are attributes. Since we can declare and define
303 // attributes all-over, we do special handling to see
304 // which definition sticks.
305 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800306 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
307 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700308 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700309 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700310 }
311
Adam Lesinskib1afa072017-03-29 13:52:38 -0700312 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700313 // Any incoming attribute is better than this.
314 return CollisionResult::kTakeNew;
315 }
316
Adam Lesinskib1afa072017-03-29 13:52:38 -0700317 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700318 // The incoming attribute may be a USE instead of a DECL.
319 // Keep the existing attribute.
320 return CollisionResult::kKeepOriginal;
321 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700322
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700323 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800324}
325
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700326ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
327 Value* /** incoming **/) {
328 return CollisionResult::kKeepBoth;
329}
330
Adam Lesinski71be7052017-12-12 16:48:07 -0800331static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700332 if (!IsValidResourceEntryName(name)) {
333 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700334 }
335 return {};
336}
337
Adam Lesinski71be7052017-12-12 16:48:07 -0800338static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700339 return {};
340}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700341
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700342bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800343 const ConfigDescription& config,
344 const StringPiece& product,
345 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700346 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700347 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
348 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
349 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700350}
351
Adam Lesinski71be7052017-12-12 16:48:07 -0800352bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
353 const ConfigDescription& config, const StringPiece& product,
354 std::unique_ptr<Value> value, IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700355 return AddResourceImpl(name, res_id, config, product, std::move(value),
356 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
357 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800358}
359
Adam Lesinski71be7052017-12-12 16:48:07 -0800360bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
361 const StringPiece& product, std::unique_ptr<Value> value,
362 IDiagnostics* diag) {
363 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700364 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700365}
366
Adam Lesinski71be7052017-12-12 16:48:07 -0800367bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
368 const ConfigDescription& config,
369 const StringPiece& product,
370 std::unique_ptr<Value> value, IDiagnostics* diag) {
371 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700372 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700373}
374
Adam Lesinski71be7052017-12-12 16:48:07 -0800375bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
376 const Source& source, IDiagnostics* diag) {
377 const StringPiece bad_char = name_validator(name.entry);
378 if (!bad_char.empty()) {
379 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
380 << name.entry << "'. Invalid character '" << bad_char << "'");
381 return false;
382 }
383 return true;
384}
385
Adam Lesinskib1afa072017-03-29 13:52:38 -0700386bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
387 const ConfigDescription& config, const StringPiece& product,
388 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800389 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700390 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700391 CHECK(value != nullptr);
392 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700393
Adam Lesinski71be7052017-12-12 16:48:07 -0800394 const Source& source = value->GetSource();
395 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700396 return false;
397 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800398
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700399 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700400 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800401 if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700402 diag->Error(DiagMessage(source)
403 << "trying to add resource '" << name << "' with ID " << res_id
404 << " but package '" << package->name << "' already has ID "
405 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700406 return false;
407 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800408
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700409 // Whether or not to error on duplicate resources
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800410 bool check_id = validate_resources_ && res_id.is_valid();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700411 // Whether or not to create a duplicate resource if the id does not match
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800412 bool use_id = !validate_resources_ && res_id.is_valid();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700413
414 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
415 : Maybe<uint8_t>());
416
417 // Check for types appearing twice with two different type ids
418 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800419 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700420 << "trying to add resource '" << name << "' with ID " << res_id
421 << " but type '" << type->type << "' already has ID "
422 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 return false;
424 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800425
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700426 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700427 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700428
429 // Check for entries appearing twice with two different entry ids
430 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800431 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700432 << "trying to add resource '" << name << "' with ID " << res_id
433 << " but resource already has ID "
434 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700435 return false;
436 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700437
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700438 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700439 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700440 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700441 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700442 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800443 switch (conflict_resolver(config_value->value.get(), value.get())) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700444 case CollisionResult::kKeepBoth:
445 // Insert the value ignoring for duplicate configurations
446 entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
447 entry->values.back()->value = std::move(value);
448 break;
449
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700450 case CollisionResult::kTakeNew:
451 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700452 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700453 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800454
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700455 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800456 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
457 << "with config '" << config << "'");
458 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700459 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700460
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461 case CollisionResult::kKeepOriginal:
462 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800463 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700464 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800465
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800466 if (res_id.is_valid()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700467 package->id = res_id.package_id();
468 type->id = res_id.type_id();
469 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700470 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700471
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700472 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800473}
474
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700475bool ResourceTable::GetValidateResources() {
476 return validate_resources_;
477}
478
Adam Lesinski71be7052017-12-12 16:48:07 -0800479bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
480 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700481 return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700482}
483
Adam Lesinski71be7052017-12-12 16:48:07 -0800484bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
485 const ResourceId& res_id, IDiagnostics* diag) {
486 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
487}
488
489bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
490 const Visibility& visibility,
491 const ResourceId& res_id, IDiagnostics* diag) {
492 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
493}
494
495bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
496 const ResourceId& res_id, NameValidator name_validator,
497 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700498 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700499
Adam Lesinski71be7052017-12-12 16:48:07 -0800500 const Source& source = visibility.source;
501 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700502 return false;
503 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800504
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700505 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700506 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800507 if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700508 diag->Error(DiagMessage(source)
509 << "trying to add resource '" << name << "' with ID " << res_id
510 << " but package '" << package->name << "' already has ID "
511 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700512 return false;
513 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800514
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700515 // Whether or not to error on duplicate resources
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800516 bool check_id = validate_resources_ && res_id.is_valid();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700517 // Whether or not to create a duplicate resource if the id does not match
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800518 bool use_id = !validate_resources_ && res_id.is_valid();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700519
520 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
521 : Maybe<uint8_t>());
522
523 // Check for types appearing twice with two different type ids
524 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800525 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700526 << "trying to add resource '" << name << "' with ID " << res_id
527 << " but type '" << type->type << "' already has ID "
528 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700529 return false;
530 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700531
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700532 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700533 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700534
535 // Check for entries appearing twice with two different entry ids
536 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800537 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700538 << "trying to add resource '" << name << "' with ID " << res_id
539 << " but resource already has ID "
540 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700541 return false;
542 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800543
Ryan Mitchellcd78feb2019-12-18 15:20:48 -0800544 if (res_id.is_valid()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700545 package->id = res_id.package_id();
546 type->id = res_id.type_id();
547 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700548 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800549
Adam Lesinski71be7052017-12-12 16:48:07 -0800550 // Only mark the type visibility level as public, it doesn't care about being private.
551 if (visibility.level == Visibility::Level::kPublic) {
552 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700553 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800554
Adam Lesinski71be7052017-12-12 16:48:07 -0800555 if (visibility.level == Visibility::Level::kUndefined &&
556 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700557 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800558 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700559 }
560
Adam Lesinski71be7052017-12-12 16:48:07 -0800561 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700562 // We can't downgrade public to private. Ignore.
563 return true;
564 }
565
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700566 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800567 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700568 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800569}
570
Adam Lesinski71be7052017-12-12 16:48:07 -0800571bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
572 IDiagnostics* diag) {
573 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
574}
575
576bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
577 IDiagnostics* diag) {
578 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
579}
580
581bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
582 NameValidator name_validator, IDiagnostics* diag) {
583 CHECK(diag != nullptr);
584
585 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
586 return false;
587 }
588
589 ResourceTablePackage* package = FindOrCreatePackage(name.package);
590 ResourceTableType* type = package->FindOrCreateType(name.type);
591 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
592 entry->allow_new = allow_new;
593 return true;
594}
595
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800596bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
Adam Lesinski71be7052017-12-12 16:48:07 -0800597 IDiagnostics* diag) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800598 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
Adam Lesinski71be7052017-12-12 16:48:07 -0800599}
600
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800601bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
602 const OverlayableItem& overlayable,
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800603 NameValidator name_validator, IDiagnostics *diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800604 CHECK(diag != nullptr);
605
606 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
607 return false;
608 }
609
610 ResourceTablePackage* package = FindOrCreatePackage(name.package);
611 ResourceTableType* type = package->FindOrCreateType(name.type);
612 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700613
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800614 if (entry->overlayable_item) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800615 diag->Error(DiagMessage(overlayable.source)
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800616 << "duplicate overlayable declaration for resource '" << name << "'");
617 diag->Error(DiagMessage(entry->overlayable_item.value().source)
618 << "previous declaration here");
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800619 return false;
Adam Lesinski71be7052017-12-12 16:48:07 -0800620 }
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700621
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800622 entry->overlayable_item = overlayable;
Adam Lesinski71be7052017-12-12 16:48:07 -0800623 return true;
624}
625
626Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700627 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800628 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700629 return {};
630 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800631
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700632 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800633 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700634 return {};
635 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800636
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700637 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800638 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700639 return {};
640 }
641 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800642}
643
Shane Farmer0a5b2012017-06-22 12:24:12 -0700644std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
645 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
646 for (const auto& pkg : packages) {
647 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
648 for (const auto& type : pkg->types) {
649 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800650 new_type->id = type->id;
651 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700652
653 for (const auto& entry : type->entries) {
654 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800655 new_entry->id = entry->id;
656 new_entry->visibility = entry->visibility;
657 new_entry->allow_new = entry->allow_new;
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800658 new_entry->overlayable_item = entry->overlayable_item;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700659
660 for (const auto& config_value : entry->values) {
661 ResourceConfigValue* new_value =
662 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800663 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700664 }
665 }
666 }
667 }
668 return new_table;
669}
670
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700671} // namespace aapt