blob: 836e199593fce317401018e70c05782ac1f46899 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskicacb28f2016-10-19 12:18:14 -070017#include "ResourceTable.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinskicacb28f2016-10-19 12:18:14 -070019#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <memory>
21#include <string>
22#include <tuple>
23
Adam Lesinski66ea8402017-06-28 11:44:11 -070024#include "android-base/logging.h"
Adam Lesinski71be7052017-12-12 16:48:07 -080025#include "android-base/stringprintf.h"
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020026#include "androidfw/ConfigDescription.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070027#include "androidfw/ResourceTypes.h"
28
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070029#include "Debug.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070030#include "NameMangler.h"
31#include "ResourceValues.h"
32#include "ValueVisitor.h"
Fabien Sanglard2d34e762019-02-21 15:13:29 -080033#include "trace/TraceBuffer.h"
Adam Lesinski66ea8402017-06-28 11:44:11 -070034#include "text/Unicode.h"
35#include "util/Util.h"
36
37using ::aapt::text::IsValidResourceEntryName;
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020038using ::android::ConfigDescription;
Adam Lesinski66ea8402017-06-28 11:44:11 -070039using ::android::StringPiece;
Adam Lesinski71be7052017-12-12 16:48:07 -080040using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080041
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080042namespace aapt {
43
Ryan Mitchell54237ff2018-12-13 15:44:29 -080044const char* Overlayable::kActorScheme = "overlay";
45
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070046static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
47 const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
48 return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080049}
50
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070052static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054}
55
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000056template <typename T>
57static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -070058 const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000059 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070060 return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000061}
62
Adam Lesinski71be7052017-12-12 16:48:07 -080063ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070064 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070065 auto iter = std::lower_bound(packages.begin(), last, name,
66 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070067 if (iter != last && name == (*iter)->name) {
68 return iter->get();
69 }
70 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071}
72
Adam Lesinski71be7052017-12-12 16:48:07 -080073ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 for (auto& package : packages) {
75 if (package->id && package->id.value() == id) {
76 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070078 }
79 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080080}
81
Adam Lesinskib1afa072017-03-29 13:52:38 -070082ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Fabien Sanglard2d34e762019-02-21 15:13:29 -080083 TRACE_CALL();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070084 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070085 if (id && !package->id) {
86 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070087 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 }
89
90 if (id && package->id && package->id.value() != id.value()) {
91 return nullptr;
92 }
93 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080094}
95
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000096ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
97 const Maybe<uint8_t> id) {
98 const auto last = packages.end();
99 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
100 less_than_struct_with_name_and_id<ResourceTablePackage>);
101
102 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
103 return iter->get();
104 }
105
106 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
107 new_package->name = name.to_string();
108 new_package->id = id;
109 return packages.emplace(iter, std::move(new_package))->get();
110}
111
Adam Lesinskib1afa072017-03-29 13:52:38 -0700112ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700113 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700114 auto iter = std::lower_bound(packages.begin(), last, name,
115 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116 if (iter != last && name == (*iter)->name) {
117 return iter->get();
118 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800119
Adam Lesinskib1afa072017-03-29 13:52:38 -0700120 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800121 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700123}
124
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700125ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700127 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
128 less_than_type_and_id);
129 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 return iter->get();
131 }
132 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133}
134
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700135ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
136 const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700137 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700138 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
139 less_than_type_and_id);
140 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 return iter->get();
142 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700143
144 auto new_type = new ResourceTableType(type);
145 new_type->id = id;
146 return types.emplace(iter, std::move(new_type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147}
148
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700149ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 const auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700151 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
152 less_than_struct_with_name_and_id<ResourceEntry>);
153 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 return iter->get();
155 }
156 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700157}
158
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700159ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700160 const Maybe<uint16_t > id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700162 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
163 less_than_struct_with_name_and_id<ResourceEntry>);
164 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 return iter->get();
166 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700167
168 auto new_entry = new ResourceEntry(name);
169 new_entry->id = id;
170 return entries.emplace(iter, std::move(new_entry))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700171}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800172
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
174 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800175}
176
177struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178 const ConfigDescription* config;
179 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800180};
181
Adam Lesinski34a16872018-02-23 16:18:10 -0800182bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700183 int cmp = lhs->config.compare(*rhs.config);
184 if (cmp == 0) {
185 cmp = StringPiece(lhs->product).compare(rhs.product);
186 }
187 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800188}
189
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700190ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800191 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800192 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
193 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 if (iter != values.end()) {
195 ResourceConfigValue* value = iter->get();
196 if (value->config == config && StringPiece(value->product) == product) {
197 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800198 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 }
200 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800201}
202
Adam Lesinskib1afa072017-03-29 13:52:38 -0700203ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
204 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800205 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
206 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 if (iter != values.end()) {
208 ResourceConfigValue* value = iter->get();
209 if (value->config == config && StringPiece(value->product) == product) {
210 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800211 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 }
213 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700214 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700215 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800216}
217
Adam Lesinskib1afa072017-03-29 13:52:38 -0700218std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700219 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800220
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700221 auto iter = values.begin();
222 for (; iter != values.end(); ++iter) {
223 ResourceConfigValue* value = iter->get();
224 if (value->config == config) {
225 results.push_back(value);
226 ++iter;
227 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800228 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700229 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800230
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700231 for (; iter != values.end(); ++iter) {
232 ResourceConfigValue* value = iter->get();
233 if (value->config == config) {
234 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800235 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 }
237 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800238}
239
Adam Lesinski34a16872018-02-23 16:18:10 -0800240bool ResourceEntry::HasDefaultValue() const {
241 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
242
243 // The default config should be at the top of the list, since the list is sorted.
244 for (auto& config_value : values) {
245 if (config_value->config == default_config) {
246 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700247 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800249 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700250}
251
Adam Lesinski71be7052017-12-12 16:48:07 -0800252// The default handler for collisions.
253//
254// Typically, a weak value will be overridden by a strong value. An existing weak
255// value will not be overridden by an incoming weak value.
256//
257// There are some exceptions:
258//
259// Attributes: There are two types of Attribute values: USE and DECL.
260//
261// USE is anywhere an Attribute is declared without a format, and in a place that would
262// be legal to declare if the Attribute already existed. This is typically in a
263// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
264//
265// DECL is an absolute declaration of an Attribute and specifies an explicit format.
266//
267// A DECL will override a USE without error. Two DECLs must match in their format for there to be
268// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700269ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
Ryan Mitchellb4351012019-03-26 16:07:23 -0700270 Value* incoming,
271 bool overlay) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700272 Attribute* existing_attr = ValueCast<Attribute>(existing);
273 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
274 if (!incoming_attr) {
275 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276 // We're trying to add a weak resource but a resource
277 // already exists. Keep the existing.
278 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700279 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700280 // Override the weak resource with the new strong resource.
281 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800282 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700283 // The existing and incoming values are strong, this is an error
284 // if the values are not both attributes.
Ryan Mitchellb4351012019-03-26 16:07:23 -0700285 return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700286 }
287
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700288 if (!existing_attr) {
289 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700290 // The existing value is not an attribute and it is weak,
291 // so take the incoming attribute value.
292 return CollisionResult::kTakeNew;
293 }
294 // The existing value is not an attribute and it is strong,
295 // so the incoming attribute value is an error.
Ryan Mitchellb4351012019-03-26 16:07:23 -0700296 return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700297 }
298
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700299 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700300
301 //
302 // Attribute specific handling. At this point we know both
303 // values are attributes. Since we can declare and define
304 // attributes all-over, we do special handling to see
305 // which definition sticks.
306 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800307 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
308 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700309 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700310 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700311 }
312
Adam Lesinskib1afa072017-03-29 13:52:38 -0700313 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700314 // Any incoming attribute is better than this.
315 return CollisionResult::kTakeNew;
316 }
317
Adam Lesinskib1afa072017-03-29 13:52:38 -0700318 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319 // The incoming attribute may be a USE instead of a DECL.
320 // Keep the existing attribute.
321 return CollisionResult::kKeepOriginal;
322 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700323
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700324 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800325}
326
Ryan Mitchellb4351012019-03-26 16:07:23 -0700327ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /* existing */,
328 Value* /* incoming */,
329 bool /* overlay */) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700330 return CollisionResult::kKeepBoth;
331}
332
Adam Lesinski71be7052017-12-12 16:48:07 -0800333static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700334 if (!IsValidResourceEntryName(name)) {
335 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700336 }
337 return {};
338}
339
Adam Lesinski71be7052017-12-12 16:48:07 -0800340static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700341 return {};
342}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700343
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700344bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800345 const ConfigDescription& config,
346 const StringPiece& product,
347 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700348 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700349 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
350 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
351 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700352}
353
Adam Lesinski71be7052017-12-12 16:48:07 -0800354bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
355 const ConfigDescription& config, const StringPiece& product,
356 std::unique_ptr<Value> value, IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700357 return AddResourceImpl(name, res_id, config, product, std::move(value),
358 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
359 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800360}
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,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700366 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), 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,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700374 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), 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
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700401 // Check for package names appearing twice with two different package ids
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()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700404 diag->Error(DiagMessage(source)
405 << "trying to add resource '" << name << "' with ID " << res_id
406 << " but package '" << package->name << "' already has ID "
407 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700408 return false;
409 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800410
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700411 // Whether or not to error on duplicate resources
412 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
413 // Whether or not to create a duplicate resource if the id does not match
414 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
415
416 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
417 : Maybe<uint8_t>());
418
419 // Check for types appearing twice with two different type ids
420 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800421 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700422 << "trying to add resource '" << name << "' with ID " << res_id
423 << " but type '" << type->type << "' already has ID "
424 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700425 return false;
426 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800427
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700428 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700429 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700430
431 // Check for entries appearing twice with two different entry ids
432 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800433 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700434 << "trying to add resource '" << name << "' with ID " << res_id
435 << " but resource already has ID "
436 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700437 return false;
438 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700439
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700440 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700441 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700442 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700443 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700444 } else {
Ryan Mitchellb4351012019-03-26 16:07:23 -0700445 switch (conflict_resolver(config_value->value.get(), value.get(), false /* overlay */)) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700446 case CollisionResult::kKeepBoth:
447 // Insert the value ignoring for duplicate configurations
448 entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
449 entry->values.back()->value = std::move(value);
450 break;
451
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700452 case CollisionResult::kTakeNew:
453 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700454 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700455 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800456
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700457 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800458 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
459 << "with config '" << config << "'");
460 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700462
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700463 case CollisionResult::kKeepOriginal:
464 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800465 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700466 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800467
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800468 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700469 package->id = res_id.package_id();
470 type->id = res_id.type_id();
471 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700472 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700473
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700474 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800475}
476
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700477bool ResourceTable::GetValidateResources() {
478 return validate_resources_;
479}
480
Adam Lesinski71be7052017-12-12 16:48:07 -0800481bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
482 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700483 return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700484}
485
Adam Lesinski71be7052017-12-12 16:48:07 -0800486bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
487 const ResourceId& res_id, IDiagnostics* diag) {
488 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
489}
490
491bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
492 const Visibility& visibility,
493 const ResourceId& res_id, IDiagnostics* diag) {
494 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
495}
496
497bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
498 const ResourceId& res_id, NameValidator name_validator,
499 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700500 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700501
Adam Lesinski71be7052017-12-12 16:48:07 -0800502 const Source& source = visibility.source;
503 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700504 return false;
505 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800506
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700507 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700508 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800509 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700510 diag->Error(DiagMessage(source)
511 << "trying to add resource '" << name << "' with ID " << res_id
512 << " but package '" << package->name << "' already has ID "
513 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700514 return false;
515 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800516
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700517 // Whether or not to error on duplicate resources
518 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
519 // Whether or not to create a duplicate resource if the id does not match
520 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
521
522 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
523 : Maybe<uint8_t>());
524
525 // Check for types appearing twice with two different type ids
526 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800527 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700528 << "trying to add resource '" << name << "' with ID " << res_id
529 << " but type '" << type->type << "' already has ID "
530 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700531 return false;
532 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700533
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700534 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700535 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700536
537 // Check for entries appearing twice with two different entry ids
538 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800539 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700540 << "trying to add resource '" << name << "' with ID " << res_id
541 << " but resource already has ID "
542 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700543 return false;
544 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800545
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800546 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700547 package->id = res_id.package_id();
548 type->id = res_id.type_id();
549 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700550 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800551
Adam Lesinski71be7052017-12-12 16:48:07 -0800552 // Only mark the type visibility level as public, it doesn't care about being private.
553 if (visibility.level == Visibility::Level::kPublic) {
554 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700555 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800556
Adam Lesinski71be7052017-12-12 16:48:07 -0800557 if (visibility.level == Visibility::Level::kUndefined &&
558 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700559 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800560 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700561 }
562
Adam Lesinski71be7052017-12-12 16:48:07 -0800563 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700564 // We can't downgrade public to private. Ignore.
565 return true;
566 }
567
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700568 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800569 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700570 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800571}
572
Adam Lesinski71be7052017-12-12 16:48:07 -0800573bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
574 IDiagnostics* diag) {
575 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
576}
577
578bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
579 IDiagnostics* diag) {
580 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
581}
582
583bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
584 NameValidator name_validator, IDiagnostics* diag) {
585 CHECK(diag != nullptr);
586
587 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
588 return false;
589 }
590
591 ResourceTablePackage* package = FindOrCreatePackage(name.package);
592 ResourceTableType* type = package->FindOrCreateType(name.type);
593 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
594 entry->allow_new = allow_new;
595 return true;
596}
597
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800598bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
Adam Lesinski71be7052017-12-12 16:48:07 -0800599 IDiagnostics* diag) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800600 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
Adam Lesinski71be7052017-12-12 16:48:07 -0800601}
602
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800603bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
604 const OverlayableItem& overlayable,
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800605 NameValidator name_validator, IDiagnostics *diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800606 CHECK(diag != nullptr);
607
608 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
609 return false;
610 }
611
612 ResourceTablePackage* package = FindOrCreatePackage(name.package);
613 ResourceTableType* type = package->FindOrCreateType(name.type);
614 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700615
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800616 if (entry->overlayable_item) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800617 diag->Error(DiagMessage(overlayable.source)
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800618 << "duplicate overlayable declaration for resource '" << name << "'");
619 diag->Error(DiagMessage(entry->overlayable_item.value().source)
620 << "previous declaration here");
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800621 return false;
Adam Lesinski71be7052017-12-12 16:48:07 -0800622 }
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700623
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800624 entry->overlayable_item = overlayable;
Adam Lesinski71be7052017-12-12 16:48:07 -0800625 return true;
626}
627
628Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700629 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800630 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700631 return {};
632 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800633
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700634 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800635 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700636 return {};
637 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800638
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700639 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800640 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700641 return {};
642 }
643 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800644}
645
Shane Farmer0a5b2012017-06-22 12:24:12 -0700646std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
647 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
648 for (const auto& pkg : packages) {
649 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
650 for (const auto& type : pkg->types) {
651 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800652 new_type->id = type->id;
653 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700654
655 for (const auto& entry : type->entries) {
656 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800657 new_entry->id = entry->id;
658 new_entry->visibility = entry->visibility;
659 new_entry->allow_new = entry->allow_new;
Ryan Mitchell54237ff2018-12-13 15:44:29 -0800660 new_entry->overlayable_item = entry->overlayable_item;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700661
662 for (const auto& config_value : entry->values) {
663 ResourceConfigValue* new_value =
664 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800665 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700666 }
667 }
668 }
669 }
670 return new_table;
671}
672
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700673} // namespace aapt