blob: d0faac30425a71a568e69c62929276c0408a7c5c [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"
29#include "NameMangler.h"
30#include "ResourceValues.h"
31#include "ValueVisitor.h"
32#include "text/Unicode.h"
33#include "util/Util.h"
34
35using ::aapt::text::IsValidResourceEntryName;
36using ::android::StringPiece;
Adam Lesinski71be7052017-12-12 16:48:07 -080037using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080038
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080039namespace aapt {
40
Adam Lesinskib1afa072017-03-29 13:52:38 -070041static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070042 return lhs->type < rhs;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043}
44
Adam Lesinski1ab598f2015-08-14 14:26:04 -070045template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070046static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070047 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080048}
49
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000050template <typename T>
51static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
52 const std::pair<StringPiece, Maybe<uint8_t>>& rhs) {
53 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
54 return name_cmp < 0 || (name_cmp == 0 && lhs->id < rhs.second);
55}
56
Adam Lesinski71be7052017-12-12 16:48:07 -080057ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070059 auto iter = std::lower_bound(packages.begin(), last, name,
60 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 if (iter != last && name == (*iter)->name) {
62 return iter->get();
63 }
64 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065}
66
Adam Lesinski71be7052017-12-12 16:48:07 -080067ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070068 for (auto& package : packages) {
69 if (package->id && package->id.value() == id) {
70 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072 }
73 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080074}
75
Adam Lesinskib1afa072017-03-29 13:52:38 -070076ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070077 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070078 if (id && !package->id) {
79 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070080 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070081 }
82
83 if (id && package->id && package->id.value() != id.value()) {
84 return nullptr;
85 }
86 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080087}
88
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000089ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
90 const Maybe<uint8_t> id) {
91 const auto last = packages.end();
92 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
93 less_than_struct_with_name_and_id<ResourceTablePackage>);
94
95 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
96 return iter->get();
97 }
98
99 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
100 new_package->name = name.to_string();
101 new_package->id = id;
102 return packages.emplace(iter, std::move(new_package))->get();
103}
104
Adam Lesinskib1afa072017-03-29 13:52:38 -0700105ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700107 auto iter = std::lower_bound(packages.begin(), last, name,
108 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700109 if (iter != last && name == (*iter)->name) {
110 return iter->get();
111 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800112
Adam Lesinskib1afa072017-03-29 13:52:38 -0700113 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800114 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700115 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700116}
117
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700118ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700119 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700120 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 if (iter != last && (*iter)->type == type) {
122 return iter->get();
123 }
124 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700125}
126
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700127ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700128 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 if (iter != last && (*iter)->type == type) {
131 return iter->get();
132 }
133 return types.emplace(iter, new ResourceTableType(type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700134}
135
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700136ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700137 const auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700138 auto iter =
139 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700140 if (iter != last && name == (*iter)->name) {
141 return iter->get();
142 }
143 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700144}
145
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700146ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700147 auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700148 auto iter =
149 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 if (iter != last && name == (*iter)->name) {
151 return iter->get();
152 }
153 return entries.emplace(iter, new ResourceEntry(name))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800155
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700156ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
157 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800158}
159
160struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 const ConfigDescription* config;
162 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800163};
164
Adam Lesinski34a16872018-02-23 16:18:10 -0800165bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 int cmp = lhs->config.compare(*rhs.config);
167 if (cmp == 0) {
168 cmp = StringPiece(lhs->product).compare(rhs.product);
169 }
170 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800171}
172
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800174 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800175 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
176 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700177 if (iter != values.end()) {
178 ResourceConfigValue* value = iter->get();
179 if (value->config == config && StringPiece(value->product) == product) {
180 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800181 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700182 }
183 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800184}
185
Adam Lesinskib1afa072017-03-29 13:52:38 -0700186ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
187 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800188 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
189 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190 if (iter != values.end()) {
191 ResourceConfigValue* value = iter->get();
192 if (value->config == config && StringPiece(value->product) == product) {
193 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800194 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700195 }
196 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700197 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700198 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800199}
200
Adam Lesinskib1afa072017-03-29 13:52:38 -0700201std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800203
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700204 auto iter = values.begin();
205 for (; iter != values.end(); ++iter) {
206 ResourceConfigValue* value = iter->get();
207 if (value->config == config) {
208 results.push_back(value);
209 ++iter;
210 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800211 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800213
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 for (; iter != values.end(); ++iter) {
215 ResourceConfigValue* value = iter->get();
216 if (value->config == config) {
217 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800218 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700219 }
220 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800221}
222
Adam Lesinski34a16872018-02-23 16:18:10 -0800223bool ResourceEntry::HasDefaultValue() const {
224 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
225
226 // The default config should be at the top of the list, since the list is sorted.
227 for (auto& config_value : values) {
228 if (config_value->config == default_config) {
229 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700230 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700231 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800232 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700233}
234
Adam Lesinski71be7052017-12-12 16:48:07 -0800235// The default handler for collisions.
236//
237// Typically, a weak value will be overridden by a strong value. An existing weak
238// value will not be overridden by an incoming weak value.
239//
240// There are some exceptions:
241//
242// Attributes: There are two types of Attribute values: USE and DECL.
243//
244// USE is anywhere an Attribute is declared without a format, and in a place that would
245// be legal to declare if the Attribute already existed. This is typically in a
246// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
247//
248// DECL is an absolute declaration of an Attribute and specifies an explicit format.
249//
250// A DECL will override a USE without error. Two DECLs must match in their format for there to be
251// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700252ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
253 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700254 Attribute* existing_attr = ValueCast<Attribute>(existing);
255 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
256 if (!incoming_attr) {
257 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700258 // We're trying to add a weak resource but a resource
259 // already exists. Keep the existing.
260 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700261 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700262 // Override the weak resource with the new strong resource.
263 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800264 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700265 // The existing and incoming values are strong, this is an error
266 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700267 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700268 }
269
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700270 if (!existing_attr) {
271 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700272 // The existing value is not an attribute and it is weak,
273 // so take the incoming attribute value.
274 return CollisionResult::kTakeNew;
275 }
276 // The existing value is not an attribute and it is strong,
277 // so the incoming attribute value is an error.
278 return CollisionResult::kConflict;
279 }
280
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700281 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700282
283 //
284 // Attribute specific handling. At this point we know both
285 // values are attributes. Since we can declare and define
286 // attributes all-over, we do special handling to see
287 // which definition sticks.
288 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800289 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
290 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700292 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700293 }
294
Adam Lesinskib1afa072017-03-29 13:52:38 -0700295 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700296 // Any incoming attribute is better than this.
297 return CollisionResult::kTakeNew;
298 }
299
Adam Lesinskib1afa072017-03-29 13:52:38 -0700300 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700301 // The incoming attribute may be a USE instead of a DECL.
302 // Keep the existing attribute.
303 return CollisionResult::kKeepOriginal;
304 }
305 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800306}
307
Adam Lesinski71be7052017-12-12 16:48:07 -0800308static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700309 if (!IsValidResourceEntryName(name)) {
310 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700311 }
312 return {};
313}
314
Adam Lesinski71be7052017-12-12 16:48:07 -0800315static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700316 return {};
317}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700318
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700319bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800320 const ConfigDescription& config,
321 const StringPiece& product,
322 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700323 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800324 return AddResourceImpl(name, {}, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700325 ResolveValueCollision, diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700326}
327
Adam Lesinski71be7052017-12-12 16:48:07 -0800328bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
329 const ConfigDescription& config, const StringPiece& product,
330 std::unique_ptr<Value> value, IDiagnostics* diag) {
331 return AddResourceImpl(name, res_id, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700332 ResolveValueCollision, diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800333}
334
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700335bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800336 const ConfigDescription& config,
337 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700338 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700339 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800340 return AddFileReferenceImpl(name, config, source, path, nullptr, ResourceNameValidator, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800341}
342
Adam Lesinski71be7052017-12-12 16:48:07 -0800343bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
344 const ConfigDescription& config, const Source& source,
345 const StringPiece& path, io::IFile* file,
346 IDiagnostics* diag) {
347 return AddFileReferenceImpl(name, config, source, path, file, SkipNameValidator, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800348}
349
Adam Lesinskib1afa072017-03-29 13:52:38 -0700350bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
351 const ConfigDescription& config, const Source& source,
352 const StringPiece& path, io::IFile* file,
353 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700354 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 util::make_unique<FileReference>(string_pool.MakeRef(path));
356 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700357 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700358 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
359 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700360}
361
Adam Lesinski71be7052017-12-12 16:48:07 -0800362bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
363 const StringPiece& product, std::unique_ptr<Value> value,
364 IDiagnostics* diag) {
365 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700366 ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700367}
368
Adam Lesinski71be7052017-12-12 16:48:07 -0800369bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
370 const ConfigDescription& config,
371 const StringPiece& product,
372 std::unique_ptr<Value> value, IDiagnostics* diag) {
373 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700374 ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700375}
376
Adam Lesinski71be7052017-12-12 16:48:07 -0800377bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
378 const Source& source, IDiagnostics* diag) {
379 const StringPiece bad_char = name_validator(name.entry);
380 if (!bad_char.empty()) {
381 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
382 << name.entry << "'. Invalid character '" << bad_char << "'");
383 return false;
384 }
385 return true;
386}
387
Adam Lesinskib1afa072017-03-29 13:52:38 -0700388bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
389 const ConfigDescription& config, const StringPiece& product,
390 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800391 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700392 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700393 CHECK(value != nullptr);
394 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700395
Adam Lesinski71be7052017-12-12 16:48:07 -0800396 const Source& source = value->GetSource();
397 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700398 return false;
399 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800400
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700401 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800402 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800403 diag->Error(DiagMessage(source) << "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
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700409 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800410 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800411 diag->Error(DiagMessage(source)
412 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
413 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700414 return false;
415 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800416
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700417 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800418 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800419 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700420 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700421 << " but resource already has ID "
Adam Lesinski71be7052017-12-12 16:48:07 -0800422 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 return false;
424 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700425
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700426 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800427 if (config_value->value == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700428 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700429 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700430 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800431 switch (conflict_resolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700432 case CollisionResult::kTakeNew:
433 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700434 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700435 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800436
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700437 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800438 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
439 << "with config '" << config << "'");
440 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700441 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700442
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700443 case CollisionResult::kKeepOriginal:
444 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800445 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700446 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800447
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800448 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700449 package->id = res_id.package_id();
450 type->id = res_id.type_id();
451 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700452 }
453 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800454}
455
Adam Lesinski71be7052017-12-12 16:48:07 -0800456bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
457 IDiagnostics* diag) {
458 return SetVisibilityImpl(name, visibility, ResourceId{}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700459}
460
Adam Lesinski71be7052017-12-12 16:48:07 -0800461bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
462 IDiagnostics* diag) {
463 return SetVisibilityImpl(name, visibility, ResourceId{}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700464}
465
Adam Lesinski71be7052017-12-12 16:48:07 -0800466bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
467 const ResourceId& res_id, IDiagnostics* diag) {
468 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
469}
470
471bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
472 const Visibility& visibility,
473 const ResourceId& res_id, IDiagnostics* diag) {
474 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
475}
476
477bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
478 const ResourceId& res_id, NameValidator name_validator,
479 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700480 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700481
Adam Lesinski71be7052017-12-12 16:48:07 -0800482 const Source& source = visibility.source;
483 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700484 return false;
485 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800486
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700487 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800488 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800489 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
490 << " but package '" << package->name << "' already has ID "
491 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700492 return false;
493 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800494
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700495 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800496 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800497 diag->Error(DiagMessage(source)
498 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
499 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700500 return false;
501 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700502
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700503 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800504 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800505 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700506 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700507 << " but resource already has ID "
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700508 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700509 return false;
510 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800511
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800512 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700513 package->id = res_id.package_id();
514 type->id = res_id.type_id();
515 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700516 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800517
Adam Lesinski71be7052017-12-12 16:48:07 -0800518 // Only mark the type visibility level as public, it doesn't care about being private.
519 if (visibility.level == Visibility::Level::kPublic) {
520 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700521 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800522
Adam Lesinski71be7052017-12-12 16:48:07 -0800523 if (visibility.level == Visibility::Level::kUndefined &&
524 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700525 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800526 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700527 }
528
Adam Lesinski71be7052017-12-12 16:48:07 -0800529 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700530 // We can't downgrade public to private. Ignore.
531 return true;
532 }
533
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700534 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800535 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700536 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800537}
538
Adam Lesinski71be7052017-12-12 16:48:07 -0800539bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
540 IDiagnostics* diag) {
541 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
542}
543
544bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
545 IDiagnostics* diag) {
546 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
547}
548
549bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
550 NameValidator name_validator, IDiagnostics* diag) {
551 CHECK(diag != nullptr);
552
553 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
554 return false;
555 }
556
557 ResourceTablePackage* package = FindOrCreatePackage(name.package);
558 ResourceTableType* type = package->FindOrCreateType(name.type);
559 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
560 entry->allow_new = allow_new;
561 return true;
562}
563
564bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
565 IDiagnostics* diag) {
566 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
567}
568
569bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
570 const Overlayable& overlayable, IDiagnostics* diag) {
571 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
572}
573
574bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
575 NameValidator name_validator, IDiagnostics* diag) {
576 CHECK(diag != nullptr);
577
578 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
579 return false;
580 }
581
582 ResourceTablePackage* package = FindOrCreatePackage(name.package);
583 ResourceTableType* type = package->FindOrCreateType(name.type);
584 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
585 if (entry->overlayable) {
586 diag->Error(DiagMessage(overlayable.source)
587 << "duplicate overlayable declaration for resource '" << name << "'");
588 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
589 return false;
590 }
591 entry->overlayable = overlayable;
592 return true;
593}
594
595Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700596 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800597 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700598 return {};
599 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800600
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700601 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800602 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700603 return {};
604 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800605
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700606 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800607 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700608 return {};
609 }
610 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800611}
612
Shane Farmer0a5b2012017-06-22 12:24:12 -0700613std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
614 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
615 for (const auto& pkg : packages) {
616 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
617 for (const auto& type : pkg->types) {
618 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800619 new_type->id = type->id;
620 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700621
622 for (const auto& entry : type->entries) {
623 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800624 new_entry->id = entry->id;
625 new_entry->visibility = entry->visibility;
626 new_entry->allow_new = entry->allow_new;
627 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700628
629 for (const auto& config_value : entry->values) {
630 ResourceConfigValue* new_value =
631 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800632 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700633 }
634 }
635 }
636 }
637 return new_table;
638}
639
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700640} // namespace aapt