blob: 3172892d7172dc48e9e106f25f83416cf25b390a [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
Adam Lesinski71be7052017-12-12 16:48:07 -080050ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070051 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070052 auto iter = std::lower_bound(packages.begin(), last, name,
53 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070054 if (iter != last && name == (*iter)->name) {
55 return iter->get();
56 }
57 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080058}
59
Adam Lesinski71be7052017-12-12 16:48:07 -080060ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 for (auto& package : packages) {
62 if (package->id && package->id.value() == id) {
63 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080064 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 }
66 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067}
68
Adam Lesinskib1afa072017-03-29 13:52:38 -070069ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070070 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070071 if (id && !package->id) {
72 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070073 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 }
75
76 if (id && package->id && package->id.value() != id.value()) {
77 return nullptr;
78 }
79 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080080}
81
Adam Lesinskib1afa072017-03-29 13:52:38 -070082ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070084 auto iter = std::lower_bound(packages.begin(), last, name,
85 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 if (iter != last && name == (*iter)->name) {
87 return iter->get();
88 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080089
Adam Lesinskib1afa072017-03-29 13:52:38 -070090 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -080091 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070092 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -070093}
94
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070096 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070098 if (iter != last && (*iter)->type == type) {
99 return iter->get();
100 }
101 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700102}
103
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700104ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700106 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700107 if (iter != last && (*iter)->type == type) {
108 return iter->get();
109 }
110 return types.emplace(iter, new ResourceTableType(type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700111}
112
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700113ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700114 const auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700115 auto iter =
116 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117 if (iter != last && name == (*iter)->name) {
118 return iter->get();
119 }
120 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121}
122
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700123ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700124 auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700125 auto iter =
126 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700127 if (iter != last && name == (*iter)->name) {
128 return iter->get();
129 }
130 return entries.emplace(iter, new ResourceEntry(name))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700131}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800132
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
134 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800135}
136
137struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700138 const ConfigDescription* config;
139 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800140};
141
Adam Lesinskib1afa072017-03-29 13:52:38 -0700142bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 int cmp = lhs->config.compare(*rhs.config);
144 if (cmp == 0) {
145 cmp = StringPiece(lhs->product).compare(rhs.product);
146 }
147 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800148}
149
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700150ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800151 const StringPiece& product) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700152 auto iter =
153 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 if (iter != values.end()) {
155 ResourceConfigValue* value = iter->get();
156 if (value->config == config && StringPiece(value->product) == product) {
157 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800158 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159 }
160 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800161}
162
Adam Lesinskib1afa072017-03-29 13:52:38 -0700163ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
164 const StringPiece& product) {
165 auto iter =
166 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700167 if (iter != values.end()) {
168 ResourceConfigValue* value = iter->get();
169 if (value->config == config && StringPiece(value->product) == product) {
170 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800171 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700172 }
173 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700174 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700175 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800176}
177
Adam Lesinskib1afa072017-03-29 13:52:38 -0700178std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700179 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800180
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700181 auto iter = values.begin();
182 for (; iter != values.end(); ++iter) {
183 ResourceConfigValue* value = iter->get();
184 if (value->config == config) {
185 results.push_back(value);
186 ++iter;
187 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800188 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700189 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800190
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700191 for (; iter != values.end(); ++iter) {
192 ResourceConfigValue* value = iter->get();
193 if (value->config == config) {
194 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800195 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700196 }
197 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800198}
199
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700200std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700201 const std::function<bool(ResourceConfigValue*)>& f) {
202 std::vector<ResourceConfigValue*> results;
203 for (auto& configValue : values) {
204 if (f(configValue.get())) {
205 results.push_back(configValue.get());
Adam Lesinski458b8772016-04-25 14:20:21 -0700206 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 }
208 return results;
Adam Lesinski458b8772016-04-25 14:20:21 -0700209}
210
Adam Lesinski71be7052017-12-12 16:48:07 -0800211// The default handler for collisions.
212//
213// Typically, a weak value will be overridden by a strong value. An existing weak
214// value will not be overridden by an incoming weak value.
215//
216// There are some exceptions:
217//
218// Attributes: There are two types of Attribute values: USE and DECL.
219//
220// USE is anywhere an Attribute is declared without a format, and in a place that would
221// be legal to declare if the Attribute already existed. This is typically in a
222// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
223//
224// DECL is an absolute declaration of an Attribute and specifies an explicit format.
225//
226// A DECL will override a USE without error. Two DECLs must match in their format for there to be
227// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700228ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
229 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700230 Attribute* existing_attr = ValueCast<Attribute>(existing);
231 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
232 if (!incoming_attr) {
233 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 // We're trying to add a weak resource but a resource
235 // already exists. Keep the existing.
236 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700237 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700238 // Override the weak resource with the new strong resource.
239 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800240 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700241 // The existing and incoming values are strong, this is an error
242 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700243 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700244 }
245
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700246 if (!existing_attr) {
247 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 // The existing value is not an attribute and it is weak,
249 // so take the incoming attribute value.
250 return CollisionResult::kTakeNew;
251 }
252 // The existing value is not an attribute and it is strong,
253 // so the incoming attribute value is an error.
254 return CollisionResult::kConflict;
255 }
256
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700257 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700258
259 //
260 // Attribute specific handling. At this point we know both
261 // values are attributes. Since we can declare and define
262 // attributes all-over, we do special handling to see
263 // which definition sticks.
264 //
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700265 if (existing_attr->type_mask == incoming_attr->type_mask) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 // The two attributes are both DECLs, but they are plain attributes
267 // with the same formats.
268 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700269 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700270 }
271
Adam Lesinskib1afa072017-03-29 13:52:38 -0700272 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700273 // Any incoming attribute is better than this.
274 return CollisionResult::kTakeNew;
275 }
276
Adam Lesinskib1afa072017-03-29 13:52:38 -0700277 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700278 // The incoming attribute may be a USE instead of a DECL.
279 // Keep the existing attribute.
280 return CollisionResult::kKeepOriginal;
281 }
282 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800283}
284
Adam Lesinski71be7052017-12-12 16:48:07 -0800285static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700286 if (!IsValidResourceEntryName(name)) {
287 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700288 }
289 return {};
290}
291
Adam Lesinski71be7052017-12-12 16:48:07 -0800292static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700293 return {};
294}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700295
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700296bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800297 const ConfigDescription& config,
298 const StringPiece& product,
299 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700300 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800301 return AddResourceImpl(name, {}, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700302 ResolveValueCollision, diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700303}
304
Adam Lesinski71be7052017-12-12 16:48:07 -0800305bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
306 const ConfigDescription& config, const StringPiece& product,
307 std::unique_ptr<Value> value, IDiagnostics* diag) {
308 return AddResourceImpl(name, res_id, config, product, std::move(value), ResourceNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700309 ResolveValueCollision, diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800310}
311
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700312bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800313 const ConfigDescription& config,
314 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700315 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700316 IDiagnostics* diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800317 return AddFileReferenceImpl(name, config, source, path, nullptr, ResourceNameValidator, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800318}
319
Adam Lesinski71be7052017-12-12 16:48:07 -0800320bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
321 const ConfigDescription& config, const Source& source,
322 const StringPiece& path, io::IFile* file,
323 IDiagnostics* diag) {
324 return AddFileReferenceImpl(name, config, source, path, file, SkipNameValidator, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800325}
326
Adam Lesinskib1afa072017-03-29 13:52:38 -0700327bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
328 const ConfigDescription& config, const Source& source,
329 const StringPiece& path, io::IFile* file,
330 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700331 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700332 util::make_unique<FileReference>(string_pool.MakeRef(path));
333 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700334 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700335 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
336 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700337}
338
Adam Lesinski71be7052017-12-12 16:48:07 -0800339bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
340 const StringPiece& product, std::unique_ptr<Value> value,
341 IDiagnostics* diag) {
342 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700343 ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700344}
345
Adam Lesinski71be7052017-12-12 16:48:07 -0800346bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
347 const ConfigDescription& config,
348 const StringPiece& product,
349 std::unique_ptr<Value> value, IDiagnostics* diag) {
350 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700351 ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700352}
353
Adam Lesinski71be7052017-12-12 16:48:07 -0800354bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
355 const Source& source, IDiagnostics* diag) {
356 const StringPiece bad_char = name_validator(name.entry);
357 if (!bad_char.empty()) {
358 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
359 << name.entry << "'. Invalid character '" << bad_char << "'");
360 return false;
361 }
362 return true;
363}
364
Adam Lesinskib1afa072017-03-29 13:52:38 -0700365bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
366 const ConfigDescription& config, const StringPiece& product,
367 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800368 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700369 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700370 CHECK(value != nullptr);
371 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700372
Adam Lesinski71be7052017-12-12 16:48:07 -0800373 const Source& source = value->GetSource();
374 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700375 return false;
376 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800377
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700378 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800379 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800380 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
381 << " but package '" << package->name << "' already has ID "
382 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700383 return false;
384 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800385
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700386 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800387 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800388 diag->Error(DiagMessage(source)
389 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
390 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700391 return false;
392 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800393
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700394 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800395 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800396 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700397 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700398 << " but resource already has ID "
Adam Lesinski71be7052017-12-12 16:48:07 -0800399 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700400 return false;
401 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700402
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700403 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800404 if (config_value->value == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700405 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700406 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700407 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800408 switch (conflict_resolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700409 case CollisionResult::kTakeNew:
410 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700411 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700412 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800413
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700414 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800415 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
416 << "with config '" << config << "'");
417 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700418 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700419
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700420 case CollisionResult::kKeepOriginal:
421 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800422 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800424
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800425 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700426 package->id = res_id.package_id();
427 type->id = res_id.type_id();
428 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700429 }
430 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800431}
432
Adam Lesinski71be7052017-12-12 16:48:07 -0800433bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
434 IDiagnostics* diag) {
435 return SetVisibilityImpl(name, visibility, ResourceId{}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700436}
437
Adam Lesinski71be7052017-12-12 16:48:07 -0800438bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
439 IDiagnostics* diag) {
440 return SetVisibilityImpl(name, visibility, ResourceId{}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700441}
442
Adam Lesinski71be7052017-12-12 16:48:07 -0800443bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
444 const ResourceId& res_id, IDiagnostics* diag) {
445 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
446}
447
448bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
449 const Visibility& visibility,
450 const ResourceId& res_id, IDiagnostics* diag) {
451 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
452}
453
454bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
455 const ResourceId& res_id, NameValidator name_validator,
456 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700457 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700458
Adam Lesinski71be7052017-12-12 16:48:07 -0800459 const Source& source = visibility.source;
460 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461 return false;
462 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800463
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700464 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800465 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800466 diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
467 << " but package '" << package->name << "' already has ID "
468 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700469 return false;
470 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800471
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700472 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800473 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800474 diag->Error(DiagMessage(source)
475 << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
476 << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700477 return false;
478 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700479
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700480 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800481 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800482 diag->Error(DiagMessage(source)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700483 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700484 << " but resource already has ID "
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700485 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700486 return false;
487 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800488
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800489 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700490 package->id = res_id.package_id();
491 type->id = res_id.type_id();
492 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700493 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800494
Adam Lesinski71be7052017-12-12 16:48:07 -0800495 // Only mark the type visibility level as public, it doesn't care about being private.
496 if (visibility.level == Visibility::Level::kPublic) {
497 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700498 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800499
Adam Lesinski71be7052017-12-12 16:48:07 -0800500 if (visibility.level == Visibility::Level::kUndefined &&
501 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700502 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800503 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700504 }
505
Adam Lesinski71be7052017-12-12 16:48:07 -0800506 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700507 // We can't downgrade public to private. Ignore.
508 return true;
509 }
510
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700511 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800512 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700513 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800514}
515
Adam Lesinski71be7052017-12-12 16:48:07 -0800516bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
517 IDiagnostics* diag) {
518 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
519}
520
521bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
522 IDiagnostics* diag) {
523 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
524}
525
526bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
527 NameValidator name_validator, IDiagnostics* diag) {
528 CHECK(diag != nullptr);
529
530 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
531 return false;
532 }
533
534 ResourceTablePackage* package = FindOrCreatePackage(name.package);
535 ResourceTableType* type = package->FindOrCreateType(name.type);
536 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
537 entry->allow_new = allow_new;
538 return true;
539}
540
541bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
542 IDiagnostics* diag) {
543 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
544}
545
546bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
547 const Overlayable& overlayable, IDiagnostics* diag) {
548 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
549}
550
551bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
552 NameValidator name_validator, IDiagnostics* diag) {
553 CHECK(diag != nullptr);
554
555 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
556 return false;
557 }
558
559 ResourceTablePackage* package = FindOrCreatePackage(name.package);
560 ResourceTableType* type = package->FindOrCreateType(name.type);
561 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
562 if (entry->overlayable) {
563 diag->Error(DiagMessage(overlayable.source)
564 << "duplicate overlayable declaration for resource '" << name << "'");
565 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
566 return false;
567 }
568 entry->overlayable = overlayable;
569 return true;
570}
571
572Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700573 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800574 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700575 return {};
576 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800577
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700578 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800579 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700580 return {};
581 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800582
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700583 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800584 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700585 return {};
586 }
587 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800588}
589
Shane Farmer0a5b2012017-06-22 12:24:12 -0700590std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
591 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
592 for (const auto& pkg : packages) {
593 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
594 for (const auto& type : pkg->types) {
595 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800596 new_type->id = type->id;
597 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700598
599 for (const auto& entry : type->entries) {
600 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800601 new_entry->id = entry->id;
602 new_entry->visibility = entry->visibility;
603 new_entry->allow_new = entry->allow_new;
604 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700605
606 for (const auto& config_value : entry->values) {
607 ResourceConfigValue* new_value =
608 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800609 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700610 }
611 }
612 }
613 }
614 return new_table;
615}
616
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700617} // namespace aapt