blob: c2274d04cc8ca3d0ce296a0b9a5b4fc3f791a349 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskicacb28f2016-10-19 12:18:14 -070017#include "ResourceTable.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinskicacb28f2016-10-19 12:18:14 -070019#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <memory>
21#include <string>
22#include <tuple>
23
Adam Lesinski66ea8402017-06-28 11:44:11 -070024#include "android-base/logging.h"
Adam Lesinski71be7052017-12-12 16:48:07 -080025#include "android-base/stringprintf.h"
Mårten Kongstad5c541f62018-06-20 08:46:41 +020026#include "androidfw/ConfigDescription.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070027#include "androidfw/ResourceTypes.h"
28
Adam Lesinski66ea8402017-06-28 11:44:11 -070029#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;
Mårten Kongstad5c541f62018-06-20 08:46:41 +020036using ::android::ConfigDescription;
Adam Lesinski66ea8402017-06-28 11:44:11 -070037using ::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
Adam Lesinskib1afa072017-03-29 13:52:38 -070042static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070043 return lhs->type < rhs;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080044}
45
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070047static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070048 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049}
50
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000051template <typename T>
52static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
53 const std::pair<StringPiece, Maybe<uint8_t>>& rhs) {
54 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
55 return name_cmp < 0 || (name_cmp == 0 && lhs->id < rhs.second);
56}
57
Adam Lesinski71be7052017-12-12 16:48:07 -080058ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070060 auto iter = std::lower_bound(packages.begin(), last, name,
61 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070062 if (iter != last && name == (*iter)->name) {
63 return iter->get();
64 }
65 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080066}
67
Adam Lesinski71be7052017-12-12 16:48:07 -080068ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070069 for (auto& package : packages) {
70 if (package->id && package->id.value() == id) {
71 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080072 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070073 }
74 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080075}
76
Adam Lesinskib1afa072017-03-29 13:52:38 -070077ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 if (id && !package->id) {
80 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070081 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070082 }
83
84 if (id && package->id && package->id.value() != id.value()) {
85 return nullptr;
86 }
87 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080088}
89
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000090ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
91 const Maybe<uint8_t> id) {
92 const auto last = packages.end();
93 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
94 less_than_struct_with_name_and_id<ResourceTablePackage>);
95
96 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
97 return iter->get();
98 }
99
100 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
101 new_package->name = name.to_string();
102 new_package->id = id;
103 return packages.emplace(iter, std::move(new_package))->get();
104}
105
Adam Lesinskib1afa072017-03-29 13:52:38 -0700106ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700107 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700108 auto iter = std::lower_bound(packages.begin(), last, name,
109 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700110 if (iter != last && name == (*iter)->name) {
111 return iter->get();
112 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800113
Adam Lesinskib1afa072017-03-29 13:52:38 -0700114 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800115 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700116 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117}
118
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700119ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700121 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700122 if (iter != last && (*iter)->type == type) {
123 return iter->get();
124 }
125 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700126}
127
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700128ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700129 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700130 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700131 if (iter != last && (*iter)->type == type) {
132 return iter->get();
133 }
134 return types.emplace(iter, new ResourceTableType(type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700135}
136
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700137ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700138 const auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700139 auto iter =
140 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 if (iter != last && name == (*iter)->name) {
142 return iter->get();
143 }
144 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145}
146
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700147ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700149 auto iter =
150 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 if (iter != last && name == (*iter)->name) {
152 return iter->get();
153 }
154 return entries.emplace(iter, new ResourceEntry(name))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700155}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800156
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700157ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
158 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800159}
160
161struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700162 const ConfigDescription* config;
163 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800164};
165
Adam Lesinski34a16872018-02-23 16:18:10 -0800166bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700167 int cmp = lhs->config.compare(*rhs.config);
168 if (cmp == 0) {
169 cmp = StringPiece(lhs->product).compare(rhs.product);
170 }
171 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800172}
173
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700174ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800175 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800176 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
177 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178 if (iter != values.end()) {
179 ResourceConfigValue* value = iter->get();
180 if (value->config == config && StringPiece(value->product) == product) {
181 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800182 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700183 }
184 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800185}
186
Adam Lesinskib1afa072017-03-29 13:52:38 -0700187ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
188 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800189 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
190 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700191 if (iter != values.end()) {
192 ResourceConfigValue* value = iter->get();
193 if (value->config == config && StringPiece(value->product) == product) {
194 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800195 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700196 }
197 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700198 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800200}
201
Adam Lesinskib1afa072017-03-29 13:52:38 -0700202std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700203 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800204
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700205 auto iter = values.begin();
206 for (; iter != values.end(); ++iter) {
207 ResourceConfigValue* value = iter->get();
208 if (value->config == config) {
209 results.push_back(value);
210 ++iter;
211 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800212 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700213 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800214
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700215 for (; iter != values.end(); ++iter) {
216 ResourceConfigValue* value = iter->get();
217 if (value->config == config) {
218 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800219 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 }
221 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800222}
223
Adam Lesinski34a16872018-02-23 16:18:10 -0800224bool ResourceEntry::HasDefaultValue() const {
225 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
226
227 // The default config should be at the top of the list, since the list is sorted.
228 for (auto& config_value : values) {
229 if (config_value->config == default_config) {
230 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700231 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700232 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800233 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700234}
235
Adam Lesinski71be7052017-12-12 16:48:07 -0800236// The default handler for collisions.
237//
238// Typically, a weak value will be overridden by a strong value. An existing weak
239// value will not be overridden by an incoming weak value.
240//
241// There are some exceptions:
242//
243// Attributes: There are two types of Attribute values: USE and DECL.
244//
245// USE is anywhere an Attribute is declared without a format, and in a place that would
246// be legal to declare if the Attribute already existed. This is typically in a
247// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
248//
249// DECL is an absolute declaration of an Attribute and specifies an explicit format.
250//
251// A DECL will override a USE without error. Two DECLs must match in their format for there to be
252// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700253ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
254 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700255 Attribute* existing_attr = ValueCast<Attribute>(existing);
256 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
257 if (!incoming_attr) {
258 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700259 // We're trying to add a weak resource but a resource
260 // already exists. Keep the existing.
261 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700262 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700263 // Override the weak resource with the new strong resource.
264 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800265 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 // The existing and incoming values are strong, this is an error
267 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700268 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700269 }
270
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700271 if (!existing_attr) {
272 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700273 // The existing value is not an attribute and it is weak,
274 // so take the incoming attribute value.
275 return CollisionResult::kTakeNew;
276 }
277 // The existing value is not an attribute and it is strong,
278 // so the incoming attribute value is an error.
279 return CollisionResult::kConflict;
280 }
281
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700282 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700283
284 //
285 // Attribute specific handling. At this point we know both
286 // values are attributes. Since we can declare and define
287 // attributes all-over, we do special handling to see
288 // which definition sticks.
289 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800290 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
291 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700292 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700293 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700294 }
295
Adam Lesinskib1afa072017-03-29 13:52:38 -0700296 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700297 // Any incoming attribute is better than this.
298 return CollisionResult::kTakeNew;
299 }
300
Adam Lesinskib1afa072017-03-29 13:52:38 -0700301 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700302 // The incoming attribute may be a USE instead of a DECL.
303 // Keep the existing attribute.
304 return CollisionResult::kKeepOriginal;
305 }
306 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800307}
308
Adam Lesinski71be7052017-12-12 16:48:07 -0800309static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700310 if (!IsValidResourceEntryName(name)) {
311 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700312 }
313 return {};
314}
315
Adam Lesinski71be7052017-12-12 16:48:07 -0800316static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700317 return {};
318}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700319
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700320bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800321 const ConfigDescription& config,
322 const StringPiece& product,
323 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700324 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800325 return AddResourceImpl(name, {}, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700326 ResolveValueCollision, diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327}
328
Adam Lesinski71be7052017-12-12 16:48:07 -0800329bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
330 const ConfigDescription& config, const StringPiece& product,
331 std::unique_ptr<Value> value, IDiagnostics* diag) {
332 return AddResourceImpl(name, res_id, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700333 ResolveValueCollision, diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800334}
335
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700336bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800337 const ConfigDescription& config,
338 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700339 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700340 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800341 return AddFileReferenceImpl(name, config, source, path, nullptr, ResourceNameValidator, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800342}
343
Adam Lesinski71be7052017-12-12 16:48:07 -0800344bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
345 const ConfigDescription& config, const Source& source,
346 const StringPiece& path, io::IFile* file,
347 IDiagnostics* diag) {
348 return AddFileReferenceImpl(name, config, source, path, file, SkipNameValidator, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800349}
350
Adam Lesinskib1afa072017-03-29 13:52:38 -0700351bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
352 const ConfigDescription& config, const Source& source,
353 const StringPiece& path, io::IFile* file,
354 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700355 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700356 util::make_unique<FileReference>(string_pool.MakeRef(path));
357 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700358 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700359 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
360 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700361}
362
Adam Lesinski71be7052017-12-12 16:48:07 -0800363bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
364 const StringPiece& product, std::unique_ptr<Value> value,
365 IDiagnostics* diag) {
366 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700367 ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700368}
369
Adam Lesinski71be7052017-12-12 16:48:07 -0800370bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
371 const ConfigDescription& config,
372 const StringPiece& product,
373 std::unique_ptr<Value> value, IDiagnostics* diag) {
374 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700375 ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700376}
377
Adam Lesinski71be7052017-12-12 16:48:07 -0800378bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
379 const Source& source, IDiagnostics* diag) {
380 const StringPiece bad_char = name_validator(name.entry);
381 if (!bad_char.empty()) {
382 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
383 << name.entry << "'. Invalid character '" << bad_char << "'");
384 return false;
385 }
386 return true;
387}
388
Adam Lesinskib1afa072017-03-29 13:52:38 -0700389bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
390 const ConfigDescription& config, const StringPiece& product,
391 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800392 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700393 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700394 CHECK(value != nullptr);
395 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700396
Adam Lesinski71be7052017-12-12 16:48:07 -0800397 const Source& source = value->GetSource();
398 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700399 return false;
400 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800401
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700402 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800403 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800404 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
405 << " but package '" << package->name << "' already has ID "
406 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700407 return false;
408 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800409
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700410 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800411 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800412 diag->Error(DiagMessage(source)
413 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
414 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700415 return false;
416 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800417
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700418 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800419 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800420 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700421 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700422 << " but resource already has ID "
Adam Lesinski71be7052017-12-12 16:48:07 -0800423 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700424 return false;
425 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700426
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700427 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800428 if (config_value->value == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700429 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700430 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800432 switch (conflict_resolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700433 case CollisionResult::kTakeNew:
434 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700435 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700436 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800437
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700438 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800439 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
440 << "with config '" << config << "'");
441 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700442 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700443
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700444 case CollisionResult::kKeepOriginal:
445 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800446 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700447 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800448
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800449 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700450 package->id = res_id.package_id();
451 type->id = res_id.type_id();
452 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700453 }
454 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800455}
456
Adam Lesinski71be7052017-12-12 16:48:07 -0800457bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
458 IDiagnostics* diag) {
459 return SetVisibilityImpl(name, visibility, ResourceId{}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700460}
461
Adam Lesinski71be7052017-12-12 16:48:07 -0800462bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
463 IDiagnostics* diag) {
464 return SetVisibilityImpl(name, visibility, ResourceId{}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700465}
466
Adam Lesinski71be7052017-12-12 16:48:07 -0800467bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
468 const ResourceId& res_id, IDiagnostics* diag) {
469 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
470}
471
472bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
473 const Visibility& visibility,
474 const ResourceId& res_id, IDiagnostics* diag) {
475 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
476}
477
478bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
479 const ResourceId& res_id, NameValidator name_validator,
480 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700481 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700482
Adam Lesinski71be7052017-12-12 16:48:07 -0800483 const Source& source = visibility.source;
484 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700485 return false;
486 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800487
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700488 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800489 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800490 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
491 << " but package '" << package->name << "' already has ID "
492 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700493 return false;
494 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800495
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700496 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800497 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800498 diag->Error(DiagMessage(source)
499 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
500 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700501 return false;
502 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700503
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700504 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800505 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800506 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700507 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700508 << " but resource already has ID "
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700509 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700510 return false;
511 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800512
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800513 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700514 package->id = res_id.package_id();
515 type->id = res_id.type_id();
516 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700517 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800518
Adam Lesinski71be7052017-12-12 16:48:07 -0800519 // Only mark the type visibility level as public, it doesn't care about being private.
520 if (visibility.level == Visibility::Level::kPublic) {
521 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700522 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800523
Adam Lesinski71be7052017-12-12 16:48:07 -0800524 if (visibility.level == Visibility::Level::kUndefined &&
525 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700526 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800527 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700528 }
529
Adam Lesinski71be7052017-12-12 16:48:07 -0800530 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700531 // We can't downgrade public to private. Ignore.
532 return true;
533 }
534
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700535 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800536 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700537 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800538}
539
Adam Lesinski71be7052017-12-12 16:48:07 -0800540bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
541 IDiagnostics* diag) {
542 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
543}
544
545bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
546 IDiagnostics* diag) {
547 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
548}
549
550bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
551 NameValidator name_validator, IDiagnostics* diag) {
552 CHECK(diag != nullptr);
553
554 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
555 return false;
556 }
557
558 ResourceTablePackage* package = FindOrCreatePackage(name.package);
559 ResourceTableType* type = package->FindOrCreateType(name.type);
560 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
561 entry->allow_new = allow_new;
562 return true;
563}
564
565bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
566 IDiagnostics* diag) {
567 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
568}
569
570bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
571 const Overlayable& overlayable, IDiagnostics* diag) {
572 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
573}
574
575bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
576 NameValidator name_validator, IDiagnostics* diag) {
577 CHECK(diag != nullptr);
578
579 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
580 return false;
581 }
582
583 ResourceTablePackage* package = FindOrCreatePackage(name.package);
584 ResourceTableType* type = package->FindOrCreateType(name.type);
585 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
586 if (entry->overlayable) {
587 diag->Error(DiagMessage(overlayable.source)
588 << "duplicate overlayable declaration for resource '" << name << "'");
589 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
590 return false;
591 }
592 entry->overlayable = overlayable;
593 return true;
594}
595
596Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700597 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800598 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700599 return {};
600 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800601
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700602 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800603 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700604 return {};
605 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800606
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700607 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800608 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700609 return {};
610 }
611 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800612}
613
Shane Farmer0a5b2012017-06-22 12:24:12 -0700614std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
615 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
616 for (const auto& pkg : packages) {
617 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
618 for (const auto& type : pkg->types) {
619 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800620 new_type->id = type->id;
621 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700622
623 for (const auto& entry : type->entries) {
624 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800625 new_entry->id = entry->id;
626 new_entry->visibility = entry->visibility;
627 new_entry->allow_new = entry->allow_new;
628 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700629
630 for (const auto& config_value : entry->values) {
631 ResourceConfigValue* new_value =
632 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800633 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700634 }
635 }
636 }
637 }
638 return new_table;
639}
640
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700641} // namespace aapt