blob: 58b5e8f0212c66b6e54e0041cd94863fcac4e0f8 [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"
Adam Lesinski66ea8402017-06-28 11:44:11 -070026#include "androidfw/ResourceTypes.h"
27
28#include "ConfigDescription.h"
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"
33#include "text/Unicode.h"
34#include "util/Util.h"
35
36using ::aapt::text::IsValidResourceEntryName;
37using ::android::StringPiece;
Adam Lesinski71be7052017-12-12 16:48:07 -080038using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080039
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080040namespace aapt {
41
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070042static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
43 const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
44 return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080045}
46
Adam Lesinski1ab598f2015-08-14 14:26:04 -070047template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070048static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080050}
51
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000052template <typename T>
53static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
54 const std::pair<StringPiece, Maybe<uint8_t>>& rhs) {
55 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070056 return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000057}
58
Adam Lesinski71be7052017-12-12 16:48:07 -080059ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070060 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070061 auto iter = std::lower_bound(packages.begin(), last, name,
62 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070063 if (iter != last && name == (*iter)->name) {
64 return iter->get();
65 }
66 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067}
68
Adam Lesinski71be7052017-12-12 16:48:07 -080069ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070070 for (auto& package : packages) {
71 if (package->id && package->id.value() == id) {
72 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080073 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 }
75 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080076}
77
Adam Lesinskib1afa072017-03-29 13:52:38 -070078ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070079 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 if (id && !package->id) {
81 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070082 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 }
84
85 if (id && package->id && package->id.value() != id.value()) {
86 return nullptr;
87 }
88 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080089}
90
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000091ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
92 const Maybe<uint8_t> id) {
93 const auto last = packages.end();
94 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
95 less_than_struct_with_name_and_id<ResourceTablePackage>);
96
97 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
98 return iter->get();
99 }
100
101 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
102 new_package->name = name.to_string();
103 new_package->id = id;
104 return packages.emplace(iter, std::move(new_package))->get();
105}
106
Adam Lesinskib1afa072017-03-29 13:52:38 -0700107ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700108 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700109 auto iter = std::lower_bound(packages.begin(), last, name,
110 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700111 if (iter != last && name == (*iter)->name) {
112 return iter->get();
113 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800114
Adam Lesinskib1afa072017-03-29 13:52:38 -0700115 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800116 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700117 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700118}
119
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700120ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700122 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
123 less_than_type_and_id);
124 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 return iter->get();
126 }
127 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128}
129
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700130ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
131 const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700133 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
134 less_than_type_and_id);
135 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 return iter->get();
137 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700138
139 auto new_type = new ResourceTableType(type);
140 new_type->id = id;
141 return types.emplace(iter, std::move(new_type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142}
143
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700144ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700145 const auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700146 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
147 less_than_struct_with_name_and_id<ResourceEntry>);
148 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 return iter->get();
150 }
151 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700152}
153
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700154ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
155 const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700156 auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700157 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
158 less_than_struct_with_name_and_id<ResourceEntry>);
159 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700160 return iter->get();
161 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700162
163 auto new_entry = new ResourceEntry(name);
164 new_entry->id = id;
165 return entries.emplace(iter, std::move(new_entry))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700166}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800167
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700168ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
169 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800170}
171
172struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700173 const ConfigDescription* config;
174 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800175};
176
Adam Lesinski34a16872018-02-23 16:18:10 -0800177bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178 int cmp = lhs->config.compare(*rhs.config);
179 if (cmp == 0) {
180 cmp = StringPiece(lhs->product).compare(rhs.product);
181 }
182 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800183}
184
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700185ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800186 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800187 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
188 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700189 if (iter != values.end()) {
190 ResourceConfigValue* value = iter->get();
191 if (value->config == config && StringPiece(value->product) == product) {
192 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800193 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 }
195 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800196}
197
Adam Lesinskib1afa072017-03-29 13:52:38 -0700198ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
199 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800200 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
201 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 if (iter != values.end()) {
203 ResourceConfigValue* value = iter->get();
204 if (value->config == config && StringPiece(value->product) == product) {
205 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800206 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 }
208 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700209 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800211}
212
Adam Lesinskib1afa072017-03-29 13:52:38 -0700213std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800215
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700216 auto iter = values.begin();
217 for (; iter != values.end(); ++iter) {
218 ResourceConfigValue* value = iter->get();
219 if (value->config == config) {
220 results.push_back(value);
221 ++iter;
222 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800223 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700224 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800225
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700226 for (; iter != values.end(); ++iter) {
227 ResourceConfigValue* value = iter->get();
228 if (value->config == config) {
229 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800230 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700231 }
232 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800233}
234
Adam Lesinski34a16872018-02-23 16:18:10 -0800235bool ResourceEntry::HasDefaultValue() const {
236 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
237
238 // The default config should be at the top of the list, since the list is sorted.
239 for (auto& config_value : values) {
240 if (config_value->config == default_config) {
241 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700242 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700243 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800244 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700245}
246
Adam Lesinski71be7052017-12-12 16:48:07 -0800247// The default handler for collisions.
248//
249// Typically, a weak value will be overridden by a strong value. An existing weak
250// value will not be overridden by an incoming weak value.
251//
252// There are some exceptions:
253//
254// Attributes: There are two types of Attribute values: USE and DECL.
255//
256// USE is anywhere an Attribute is declared without a format, and in a place that would
257// be legal to declare if the Attribute already existed. This is typically in a
258// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
259//
260// DECL is an absolute declaration of an Attribute and specifies an explicit format.
261//
262// A DECL will override a USE without error. Two DECLs must match in their format for there to be
263// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700264ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
265 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700266 Attribute* existing_attr = ValueCast<Attribute>(existing);
267 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
268 if (!incoming_attr) {
269 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700270 // We're trying to add a weak resource but a resource
271 // already exists. Keep the existing.
272 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700273 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 // Override the weak resource with the new strong resource.
275 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800276 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700277 // The existing and incoming values are strong, this is an error
278 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700279 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700280 }
281
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700282 if (!existing_attr) {
283 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700284 // The existing value is not an attribute and it is weak,
285 // so take the incoming attribute value.
286 return CollisionResult::kTakeNew;
287 }
288 // The existing value is not an attribute and it is strong,
289 // so the incoming attribute value is an error.
290 return CollisionResult::kConflict;
291 }
292
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700293 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700294
295 //
296 // Attribute specific handling. At this point we know both
297 // values are attributes. Since we can declare and define
298 // attributes all-over, we do special handling to see
299 // which definition sticks.
300 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800301 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
302 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700304 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700305 }
306
Adam Lesinskib1afa072017-03-29 13:52:38 -0700307 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700308 // Any incoming attribute is better than this.
309 return CollisionResult::kTakeNew;
310 }
311
Adam Lesinskib1afa072017-03-29 13:52:38 -0700312 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700313 // The incoming attribute may be a USE instead of a DECL.
314 // Keep the existing attribute.
315 return CollisionResult::kKeepOriginal;
316 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700317
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700318 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800319}
320
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700321ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
322 Value* /** incoming **/) {
323 return CollisionResult::kKeepBoth;
324}
325
Adam Lesinski71be7052017-12-12 16:48:07 -0800326static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700327 if (!IsValidResourceEntryName(name)) {
328 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700329 }
330 return {};
331}
332
Adam Lesinski71be7052017-12-12 16:48:07 -0800333static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700334 return {};
335}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700336
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700337bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800338 const ConfigDescription& config,
339 const StringPiece& product,
340 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700341 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700342 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
343 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
344 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700345}
346
Adam Lesinski71be7052017-12-12 16:48:07 -0800347bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
348 const ConfigDescription& config, const StringPiece& product,
349 std::unique_ptr<Value> value, IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700350 return AddResourceImpl(name, res_id, config, product, std::move(value),
351 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
352 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800353}
354
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800356 const ConfigDescription& config,
357 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700358 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700360 return AddFileReferenceImpl(name, config, source, path, nullptr,
361 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
362 diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800363}
364
Adam Lesinski71be7052017-12-12 16:48:07 -0800365bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
366 const ConfigDescription& config, const Source& source,
367 const StringPiece& path, io::IFile* file,
368 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700369 return AddFileReferenceImpl(name, config, source, path, file,
370 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
371 diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800372}
373
Adam Lesinskib1afa072017-03-29 13:52:38 -0700374bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
375 const ConfigDescription& config, const Source& source,
376 const StringPiece& path, io::IFile* file,
377 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700378 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700379 util::make_unique<FileReference>(string_pool.MakeRef(path));
380 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700381 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700382 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
383 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700384}
385
Adam Lesinski71be7052017-12-12 16:48:07 -0800386bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
387 const StringPiece& product, std::unique_ptr<Value> value,
388 IDiagnostics* diag) {
389 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700390 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700391}
392
Adam Lesinski71be7052017-12-12 16:48:07 -0800393bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
394 const ConfigDescription& config,
395 const StringPiece& product,
396 std::unique_ptr<Value> value, IDiagnostics* diag) {
397 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700398 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700399}
400
Adam Lesinski71be7052017-12-12 16:48:07 -0800401bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
402 const Source& source, IDiagnostics* diag) {
403 const StringPiece bad_char = name_validator(name.entry);
404 if (!bad_char.empty()) {
405 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
406 << name.entry << "'. Invalid character '" << bad_char << "'");
407 return false;
408 }
409 return true;
410}
411
Adam Lesinskib1afa072017-03-29 13:52:38 -0700412bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
413 const ConfigDescription& config, const StringPiece& product,
414 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800415 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700416 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700417 CHECK(value != nullptr);
418 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700419
Adam Lesinski71be7052017-12-12 16:48:07 -0800420 const Source& source = value->GetSource();
421 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700422 return false;
423 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800424
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700425 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700426 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800427 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700428 diag->Error(DiagMessage(source)
429 << "trying to add resource '" << name << "' with ID " << res_id
430 << " but package '" << package->name << "' already has ID "
431 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700432 return false;
433 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800434
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700435 // Whether or not to error on duplicate resources
436 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
437 // Whether or not to create a duplicate resource if the id does not match
438 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
439
440 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
441 : Maybe<uint8_t>());
442
443 // Check for types appearing twice with two different type ids
444 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800445 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700446 << "trying to add resource '" << name << "' with ID " << res_id
447 << " but type '" << type->type << "' already has ID "
448 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700449 return false;
450 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800451
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700452 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
453 : Maybe<uint8_t>());
454
455 // Check for entries appearing twice with two different entry ids
456 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800457 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700458 << "trying to add resource '" << name << "' with ID " << res_id
459 << " but resource already has ID "
460 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461 return false;
462 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700463
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700464 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700465 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700466 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700467 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700468 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800469 switch (conflict_resolver(config_value->value.get(), value.get())) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700470 case CollisionResult::kKeepBoth:
471 // Insert the value ignoring for duplicate configurations
472 entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
473 entry->values.back()->value = std::move(value);
474 break;
475
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700476 case CollisionResult::kTakeNew:
477 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700478 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700479 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800480
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700481 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800482 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
483 << "with config '" << config << "'");
484 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700485 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700486
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700487 case CollisionResult::kKeepOriginal:
488 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800489 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700490 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800491
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800492 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700493 package->id = res_id.package_id();
494 type->id = res_id.type_id();
495 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700496 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700497
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700498 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800499}
500
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700501bool ResourceTable::GetValidateResources() {
502 return validate_resources_;
503}
504
Adam Lesinski71be7052017-12-12 16:48:07 -0800505bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
506 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700507 return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700508}
509
Adam Lesinski71be7052017-12-12 16:48:07 -0800510bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
511 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700512 return SetVisibilityImpl(name, visibility, {}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700513}
514
Adam Lesinski71be7052017-12-12 16:48:07 -0800515bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
516 const ResourceId& res_id, IDiagnostics* diag) {
517 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
518}
519
520bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
521 const Visibility& visibility,
522 const ResourceId& res_id, IDiagnostics* diag) {
523 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
524}
525
526bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
527 const ResourceId& res_id, NameValidator name_validator,
528 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700529 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700530
Adam Lesinski71be7052017-12-12 16:48:07 -0800531 const Source& source = visibility.source;
532 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700533 return false;
534 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800535
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700536 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700537 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800538 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700539 diag->Error(DiagMessage(source)
540 << "trying to add resource '" << name << "' with ID " << res_id
541 << " but package '" << package->name << "' already has ID "
542 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700543 return false;
544 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800545
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700546 // Whether or not to error on duplicate resources
547 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
548 // Whether or not to create a duplicate resource if the id does not match
549 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
550
551 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
552 : Maybe<uint8_t>());
553
554 // Check for types appearing twice with two different type ids
555 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800556 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700557 << "trying to add resource '" << name << "' with ID " << res_id
558 << " but type '" << type->type << "' already has ID "
559 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700560 return false;
561 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700562
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700563 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
564 : Maybe<uint8_t>());
565
566 // Check for entries appearing twice with two different entry ids
567 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800568 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700569 << "trying to add resource '" << name << "' with ID " << res_id
570 << " but resource already has ID "
571 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700572 return false;
573 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800574
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800575 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700576 package->id = res_id.package_id();
577 type->id = res_id.type_id();
578 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700579 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800580
Adam Lesinski71be7052017-12-12 16:48:07 -0800581 // Only mark the type visibility level as public, it doesn't care about being private.
582 if (visibility.level == Visibility::Level::kPublic) {
583 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700584 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800585
Adam Lesinski71be7052017-12-12 16:48:07 -0800586 if (visibility.level == Visibility::Level::kUndefined &&
587 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700588 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800589 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700590 }
591
Adam Lesinski71be7052017-12-12 16:48:07 -0800592 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700593 // We can't downgrade public to private. Ignore.
594 return true;
595 }
596
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700597 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800598 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700599 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800600}
601
Adam Lesinski71be7052017-12-12 16:48:07 -0800602bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
603 IDiagnostics* diag) {
604 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
605}
606
607bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
608 IDiagnostics* diag) {
609 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
610}
611
612bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
613 NameValidator name_validator, IDiagnostics* diag) {
614 CHECK(diag != nullptr);
615
616 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
617 return false;
618 }
619
620 ResourceTablePackage* package = FindOrCreatePackage(name.package);
621 ResourceTableType* type = package->FindOrCreateType(name.type);
622 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
623 entry->allow_new = allow_new;
624 return true;
625}
626
627bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
628 IDiagnostics* diag) {
629 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
630}
631
632bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
633 const Overlayable& overlayable, IDiagnostics* diag) {
634 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
635}
636
637bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
638 NameValidator name_validator, IDiagnostics* diag) {
639 CHECK(diag != nullptr);
640
641 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
642 return false;
643 }
644
645 ResourceTablePackage* package = FindOrCreatePackage(name.package);
646 ResourceTableType* type = package->FindOrCreateType(name.type);
647 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
648 if (entry->overlayable) {
649 diag->Error(DiagMessage(overlayable.source)
650 << "duplicate overlayable declaration for resource '" << name << "'");
651 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
652 return false;
653 }
654 entry->overlayable = overlayable;
655 return true;
656}
657
658Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700659 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800660 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700661 return {};
662 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800663
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700664 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800665 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700666 return {};
667 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800668
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700669 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800670 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700671 return {};
672 }
673 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800674}
675
Shane Farmer0a5b2012017-06-22 12:24:12 -0700676std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
677 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
678 for (const auto& pkg : packages) {
679 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
680 for (const auto& type : pkg->types) {
681 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800682 new_type->id = type->id;
683 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700684
685 for (const auto& entry : type->entries) {
686 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800687 new_entry->id = entry->id;
688 new_entry->visibility = entry->visibility;
689 new_entry->allow_new = entry->allow_new;
690 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700691
692 for (const auto& config_value : entry->values) {
693 ResourceConfigValue* new_value =
694 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800695 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700696 }
697 }
698 }
699 }
700 return new_table;
701}
702
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700703} // namespace aapt