blob: 95bf9210ba97fe4f043160cd00b23f84e82c00df [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 Lesinskib1afa072017-03-29 13:52:38 -0700165bool ltConfigKeyRef(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 Lesinskib1afa072017-03-29 13:52:38 -0700175 auto iter =
176 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
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) {
188 auto iter =
189 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
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 Lesinskice5e56e2016-10-21 17:56:45 -0700223std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700224 const std::function<bool(ResourceConfigValue*)>& f) {
225 std::vector<ResourceConfigValue*> results;
226 for (auto& configValue : values) {
227 if (f(configValue.get())) {
228 results.push_back(configValue.get());
Adam Lesinski458b8772016-04-25 14:20:21 -0700229 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700230 }
231 return results;
Adam Lesinski458b8772016-04-25 14:20:21 -0700232}
233
Adam Lesinski71be7052017-12-12 16:48:07 -0800234// The default handler for collisions.
235//
236// Typically, a weak value will be overridden by a strong value. An existing weak
237// value will not be overridden by an incoming weak value.
238//
239// There are some exceptions:
240//
241// Attributes: There are two types of Attribute values: USE and DECL.
242//
243// USE is anywhere an Attribute is declared without a format, and in a place that would
244// be legal to declare if the Attribute already existed. This is typically in a
245// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
246//
247// DECL is an absolute declaration of an Attribute and specifies an explicit format.
248//
249// A DECL will override a USE without error. Two DECLs must match in their format for there to be
250// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700251ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
252 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700253 Attribute* existing_attr = ValueCast<Attribute>(existing);
254 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
255 if (!incoming_attr) {
256 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700257 // We're trying to add a weak resource but a resource
258 // already exists. Keep the existing.
259 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700260 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700261 // Override the weak resource with the new strong resource.
262 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800263 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700264 // The existing and incoming values are strong, this is an error
265 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700266 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700267 }
268
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700269 if (!existing_attr) {
270 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 // The existing value is not an attribute and it is weak,
272 // so take the incoming attribute value.
273 return CollisionResult::kTakeNew;
274 }
275 // The existing value is not an attribute and it is strong,
276 // so the incoming attribute value is an error.
277 return CollisionResult::kConflict;
278 }
279
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700280 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700281
282 //
283 // Attribute specific handling. At this point we know both
284 // values are attributes. Since we can declare and define
285 // attributes all-over, we do special handling to see
286 // which definition sticks.
287 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800288 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
289 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700290 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700291 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700292 }
293
Adam Lesinskib1afa072017-03-29 13:52:38 -0700294 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700295 // Any incoming attribute is better than this.
296 return CollisionResult::kTakeNew;
297 }
298
Adam Lesinskib1afa072017-03-29 13:52:38 -0700299 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700300 // The incoming attribute may be a USE instead of a DECL.
301 // Keep the existing attribute.
302 return CollisionResult::kKeepOriginal;
303 }
304 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800305}
306
Adam Lesinski71be7052017-12-12 16:48:07 -0800307static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700308 if (!IsValidResourceEntryName(name)) {
309 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700310 }
311 return {};
312}
313
Adam Lesinski71be7052017-12-12 16:48:07 -0800314static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700315 return {};
316}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700317
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800319 const ConfigDescription& config,
320 const StringPiece& product,
321 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700322 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800323 return AddResourceImpl(name, {}, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700324 ResolveValueCollision, diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325}
326
Adam Lesinski71be7052017-12-12 16:48:07 -0800327bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
328 const ConfigDescription& config, const StringPiece& product,
329 std::unique_ptr<Value> value, IDiagnostics* diag) {
330 return AddResourceImpl(name, res_id, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700331 ResolveValueCollision, diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800332}
333
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700334bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800335 const ConfigDescription& config,
336 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700337 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700338 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800339 return AddFileReferenceImpl(name, config, source, path, nullptr, ResourceNameValidator, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800340}
341
Adam Lesinski71be7052017-12-12 16:48:07 -0800342bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
343 const ConfigDescription& config, const Source& source,
344 const StringPiece& path, io::IFile* file,
345 IDiagnostics* diag) {
346 return AddFileReferenceImpl(name, config, source, path, file, SkipNameValidator, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800347}
348
Adam Lesinskib1afa072017-03-29 13:52:38 -0700349bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
350 const ConfigDescription& config, const Source& source,
351 const StringPiece& path, io::IFile* file,
352 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700353 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700354 util::make_unique<FileReference>(string_pool.MakeRef(path));
355 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700356 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700357 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
358 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700359}
360
Adam Lesinski71be7052017-12-12 16:48:07 -0800361bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
362 const StringPiece& product, std::unique_ptr<Value> value,
363 IDiagnostics* diag) {
364 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700365 ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700366}
367
Adam Lesinski71be7052017-12-12 16:48:07 -0800368bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
369 const ConfigDescription& config,
370 const StringPiece& product,
371 std::unique_ptr<Value> value, IDiagnostics* diag) {
372 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700373 ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700374}
375
Adam Lesinski71be7052017-12-12 16:48:07 -0800376bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
377 const Source& source, IDiagnostics* diag) {
378 const StringPiece bad_char = name_validator(name.entry);
379 if (!bad_char.empty()) {
380 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
381 << name.entry << "'. Invalid character '" << bad_char << "'");
382 return false;
383 }
384 return true;
385}
386
Adam Lesinskib1afa072017-03-29 13:52:38 -0700387bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
388 const ConfigDescription& config, const StringPiece& product,
389 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800390 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700391 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700392 CHECK(value != nullptr);
393 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700394
Adam Lesinski71be7052017-12-12 16:48:07 -0800395 const Source& source = value->GetSource();
396 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700397 return false;
398 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800399
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700400 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800401 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800402 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
403 << " but package '" << package->name << "' already has ID "
404 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700405 return false;
406 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800407
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700408 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800409 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800410 diag->Error(DiagMessage(source)
411 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
412 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700413 return false;
414 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800415
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700416 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800417 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800418 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700419 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700420 << " but resource already has ID "
Adam Lesinski71be7052017-12-12 16:48:07 -0800421 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700422 return false;
423 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700424
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700425 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800426 if (config_value->value == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700427 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700428 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700429 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800430 switch (conflict_resolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 case CollisionResult::kTakeNew:
432 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700433 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700434 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800435
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700436 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800437 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
438 << "with config '" << config << "'");
439 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700440 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700441
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700442 case CollisionResult::kKeepOriginal:
443 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800444 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700445 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800446
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800447 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700448 package->id = res_id.package_id();
449 type->id = res_id.type_id();
450 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700451 }
452 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800453}
454
Adam Lesinski71be7052017-12-12 16:48:07 -0800455bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
456 IDiagnostics* diag) {
457 return SetVisibilityImpl(name, visibility, ResourceId{}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700458}
459
Adam Lesinski71be7052017-12-12 16:48:07 -0800460bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
461 IDiagnostics* diag) {
462 return SetVisibilityImpl(name, visibility, ResourceId{}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700463}
464
Adam Lesinski71be7052017-12-12 16:48:07 -0800465bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
466 const ResourceId& res_id, IDiagnostics* diag) {
467 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
468}
469
470bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
471 const Visibility& visibility,
472 const ResourceId& res_id, IDiagnostics* diag) {
473 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
474}
475
476bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
477 const ResourceId& res_id, NameValidator name_validator,
478 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700479 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700480
Adam Lesinski71be7052017-12-12 16:48:07 -0800481 const Source& source = visibility.source;
482 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700483 return false;
484 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800485
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700486 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800487 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800488 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
489 << " but package '" << package->name << "' already has ID "
490 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700491 return false;
492 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800493
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700494 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800495 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800496 diag->Error(DiagMessage(source)
497 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
498 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700499 return false;
500 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700501
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700502 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800503 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800504 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700505 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700506 << " but resource already has ID "
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700507 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700508 return false;
509 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800510
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800511 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700512 package->id = res_id.package_id();
513 type->id = res_id.type_id();
514 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700515 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800516
Adam Lesinski71be7052017-12-12 16:48:07 -0800517 // Only mark the type visibility level as public, it doesn't care about being private.
518 if (visibility.level == Visibility::Level::kPublic) {
519 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700520 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800521
Adam Lesinski71be7052017-12-12 16:48:07 -0800522 if (visibility.level == Visibility::Level::kUndefined &&
523 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700524 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800525 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700526 }
527
Adam Lesinski71be7052017-12-12 16:48:07 -0800528 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700529 // We can't downgrade public to private. Ignore.
530 return true;
531 }
532
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700533 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800534 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700535 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800536}
537
Adam Lesinski71be7052017-12-12 16:48:07 -0800538bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
539 IDiagnostics* diag) {
540 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
541}
542
543bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
544 IDiagnostics* diag) {
545 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
546}
547
548bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
549 NameValidator name_validator, IDiagnostics* diag) {
550 CHECK(diag != nullptr);
551
552 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
553 return false;
554 }
555
556 ResourceTablePackage* package = FindOrCreatePackage(name.package);
557 ResourceTableType* type = package->FindOrCreateType(name.type);
558 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
559 entry->allow_new = allow_new;
560 return true;
561}
562
563bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
564 IDiagnostics* diag) {
565 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
566}
567
568bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
569 const Overlayable& overlayable, IDiagnostics* diag) {
570 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
571}
572
573bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
574 NameValidator name_validator, IDiagnostics* diag) {
575 CHECK(diag != nullptr);
576
577 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
578 return false;
579 }
580
581 ResourceTablePackage* package = FindOrCreatePackage(name.package);
582 ResourceTableType* type = package->FindOrCreateType(name.type);
583 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
584 if (entry->overlayable) {
585 diag->Error(DiagMessage(overlayable.source)
586 << "duplicate overlayable declaration for resource '" << name << "'");
587 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
588 return false;
589 }
590 entry->overlayable = overlayable;
591 return true;
592}
593
594Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700595 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800596 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700597 return {};
598 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800599
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700600 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800601 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700602 return {};
603 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800604
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700605 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800606 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700607 return {};
608 }
609 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800610}
611
Shane Farmer0a5b2012017-06-22 12:24:12 -0700612std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
613 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
614 for (const auto& pkg : packages) {
615 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
616 for (const auto& type : pkg->types) {
617 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800618 new_type->id = type->id;
619 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700620
621 for (const auto& entry : type->entries) {
622 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800623 new_entry->id = entry->id;
624 new_entry->visibility = entry->visibility;
625 new_entry->allow_new = entry->allow_new;
626 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700627
628 for (const auto& config_value : entry->values) {
629 ResourceConfigValue* new_value =
630 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800631 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700632 }
633 }
634 }
635 }
636 return new_table;
637}
638
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700639} // namespace aapt