1 /*
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
17 #include "ResourceTable.h"
18
19 #include <algorithm>
20 #include <memory>
21 #include <string>
22 #include <tuple>
23
24 #include "android-base/logging.h"
25 #include "android-base/stringprintf.h"
26 #include "androidfw/ConfigDescription.h"
27 #include "androidfw/ResourceTypes.h"
28
29 #include "Debug.h"
30 #include "NameMangler.h"
31 #include "ResourceValues.h"
32 #include "ValueVisitor.h"
33 #include "trace/TraceBuffer.h"
34 #include "text/Unicode.h"
35 #include "util/Util.h"
36
37 using ::aapt::text::IsValidResourceEntryName;
38 using ::android::ConfigDescription;
39 using ::android::StringPiece;
40 using ::android::base::StringPrintf;
41
42 namespace aapt {
43
44 const char* Overlayable::kActorScheme = "overlay";
45
less_than_type_and_id(const std::unique_ptr<ResourceTableType> & lhs,const std::pair<ResourceType,Maybe<uint8_t>> & rhs)46 static 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);
49 }
50
51 template <typename T>
less_than_struct_with_name(const std::unique_ptr<T> & lhs,const StringPiece & rhs)52 static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
53 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
54 }
55
56 template <typename T>
less_than_struct_with_name_and_id(const std::unique_ptr<T> & lhs,const std::pair<StringPiece,Maybe<uint16_t>> & rhs)57 static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
58 const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
59 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
60 return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
61 }
62
FindPackage(const StringPiece & name) const63 ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
64 const auto last = packages.end();
65 auto iter = std::lower_bound(packages.begin(), last, name,
66 less_than_struct_with_name<ResourceTablePackage>);
67 if (iter != last && name == (*iter)->name) {
68 return iter->get();
69 }
70 return nullptr;
71 }
72
FindPackageById(uint8_t id) const73 ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
74 for (auto& package : packages) {
75 if (package->id && package->id.value() == id) {
76 return package.get();
77 }
78 }
79 return nullptr;
80 }
81
CreatePackage(const StringPiece & name,Maybe<uint8_t> id)82 ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
83 TRACE_CALL();
84 ResourceTablePackage* package = FindOrCreatePackage(name);
85 if (id && !package->id) {
86 package->id = id;
87 return package;
88 }
89
90 if (id && package->id && package->id.value() != id.value()) {
91 return nullptr;
92 }
93 return package;
94 }
95
CreatePackageAllowingDuplicateNames(const StringPiece & name,const Maybe<uint8_t> id)96 ResourceTablePackage* 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
FindOrCreatePackage(const StringPiece & name)112 ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
113 const auto last = packages.end();
114 auto iter = std::lower_bound(packages.begin(), last, name,
115 less_than_struct_with_name<ResourceTablePackage>);
116 if (iter != last && name == (*iter)->name) {
117 return iter->get();
118 }
119
120 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
121 new_package->name = name.to_string();
122 return packages.emplace(iter, std::move(new_package))->get();
123 }
124
FindType(ResourceType type,const Maybe<uint8_t> id)125 ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
126 const auto last = types.end();
127 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)) {
130 return iter->get();
131 }
132 return nullptr;
133 }
134
FindOrCreateType(ResourceType type,const Maybe<uint8_t> id)135 ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
136 const Maybe<uint8_t> id) {
137 const auto last = types.end();
138 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)) {
141 return iter->get();
142 }
143
144 auto new_type = new ResourceTableType(type);
145 new_type->id = id;
146 return types.emplace(iter, std::move(new_type))->get();
147 }
148
FindEntry(const StringPiece & name,const Maybe<uint16_t> id)149 ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
150 const auto last = entries.end();
151 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)) {
154 return iter->get();
155 }
156 return nullptr;
157 }
158
FindOrCreateEntry(const StringPiece & name,const Maybe<uint16_t> id)159 ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
160 const Maybe<uint16_t > id) {
161 auto last = entries.end();
162 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)) {
165 return iter->get();
166 }
167
168 auto new_entry = new ResourceEntry(name);
169 new_entry->id = id;
170 return entries.emplace(iter, std::move(new_entry))->get();
171 }
172
FindValue(const ConfigDescription & config)173 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
174 return FindValue(config, StringPiece());
175 }
176
177 struct ConfigKey {
178 const ConfigDescription* config;
179 const StringPiece& product;
180 };
181
lt_config_key_ref(const std::unique_ptr<ResourceConfigValue> & lhs,const ConfigKey & rhs)182 bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
183 int cmp = lhs->config.compare(*rhs.config);
184 if (cmp == 0) {
185 cmp = StringPiece(lhs->product).compare(rhs.product);
186 }
187 return cmp < 0;
188 }
189
FindValue(const ConfigDescription & config,const StringPiece & product)190 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
191 const StringPiece& product) {
192 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
193 lt_config_key_ref);
194 if (iter != values.end()) {
195 ResourceConfigValue* value = iter->get();
196 if (value->config == config && StringPiece(value->product) == product) {
197 return value;
198 }
199 }
200 return nullptr;
201 }
202
FindOrCreateValue(const ConfigDescription & config,const StringPiece & product)203 ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
204 const StringPiece& product) {
205 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
206 lt_config_key_ref);
207 if (iter != values.end()) {
208 ResourceConfigValue* value = iter->get();
209 if (value->config == config && StringPiece(value->product) == product) {
210 return value;
211 }
212 }
213 ResourceConfigValue* newValue =
214 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
215 return newValue;
216 }
217
FindAllValues(const ConfigDescription & config)218 std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
219 std::vector<ResourceConfigValue*> results;
220
221 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;
228 }
229 }
230
231 for (; iter != values.end(); ++iter) {
232 ResourceConfigValue* value = iter->get();
233 if (value->config == config) {
234 results.push_back(value);
235 }
236 }
237 return results;
238 }
239
HasDefaultValue() const240 bool 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;
247 }
248 }
249 return false;
250 }
251
252 // 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.
ResolveValueCollision(Value * existing,Value * incoming)269 ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
270 Value* incoming) {
271 Attribute* existing_attr = ValueCast<Attribute>(existing);
272 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
273 if (!incoming_attr) {
274 if (incoming->IsWeak()) {
275 // We're trying to add a weak resource but a resource
276 // already exists. Keep the existing.
277 return CollisionResult::kKeepOriginal;
278 } else if (existing->IsWeak()) {
279 // Override the weak resource with the new strong resource.
280 return CollisionResult::kTakeNew;
281 }
282 // The existing and incoming values are strong, this is an error
283 // if the values are not both attributes.
284 return CollisionResult::kConflict;
285 }
286
287 if (!existing_attr) {
288 if (existing->IsWeak()) {
289 // The existing value is not an attribute and it is weak,
290 // so take the incoming attribute value.
291 return CollisionResult::kTakeNew;
292 }
293 // The existing value is not an attribute and it is strong,
294 // so the incoming attribute value is an error.
295 return CollisionResult::kConflict;
296 }
297
298 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
299
300 //
301 // Attribute specific handling. At this point we know both
302 // values are attributes. Since we can declare and define
303 // attributes all-over, we do special handling to see
304 // which definition sticks.
305 //
306 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
307 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
308 // Keep the strongest one.
309 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
310 }
311
312 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
313 // Any incoming attribute is better than this.
314 return CollisionResult::kTakeNew;
315 }
316
317 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
318 // The incoming attribute may be a USE instead of a DECL.
319 // Keep the existing attribute.
320 return CollisionResult::kKeepOriginal;
321 }
322
323 return CollisionResult::kConflict;
324 }
325
IgnoreCollision(Value *,Value *)326 ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
327 Value* /** incoming **/) {
328 return CollisionResult::kKeepBoth;
329 }
330
ResourceNameValidator(const StringPiece & name)331 static StringPiece ResourceNameValidator(const StringPiece& name) {
332 if (!IsValidResourceEntryName(name)) {
333 return name;
334 }
335 return {};
336 }
337
SkipNameValidator(const StringPiece &)338 static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
339 return {};
340 }
341
AddResource(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)342 bool ResourceTable::AddResource(const ResourceNameRef& name,
343 const ConfigDescription& config,
344 const StringPiece& product,
345 std::unique_ptr<Value> value,
346 IDiagnostics* diag) {
347 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
348 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
349 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
350 }
351
AddResourceWithId(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)352 bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
353 const ConfigDescription& config, const StringPiece& product,
354 std::unique_ptr<Value> value, IDiagnostics* diag) {
355 return AddResourceImpl(name, res_id, config, product, std::move(value),
356 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
357 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
358 }
359
AddResourceMangled(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)360 bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
361 const StringPiece& product, std::unique_ptr<Value> value,
362 IDiagnostics* diag) {
363 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
364 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
365 }
366
AddResourceWithIdMangled(const ResourceNameRef & name,const ResourceId & id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)367 bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
368 const ConfigDescription& config,
369 const StringPiece& product,
370 std::unique_ptr<Value> value, IDiagnostics* diag) {
371 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
372 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
373 }
374
ValidateName(NameValidator name_validator,const ResourceNameRef & name,const Source & source,IDiagnostics * diag)375 bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
376 const Source& source, IDiagnostics* diag) {
377 const StringPiece bad_char = name_validator(name.entry);
378 if (!bad_char.empty()) {
379 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
380 << name.entry << "'. Invalid character '" << bad_char << "'");
381 return false;
382 }
383 return true;
384 }
385
AddResourceImpl(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,NameValidator name_validator,const CollisionResolverFunc & conflict_resolver,IDiagnostics * diag)386 bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
387 const ConfigDescription& config, const StringPiece& product,
388 std::unique_ptr<Value> value, NameValidator name_validator,
389 const CollisionResolverFunc& conflict_resolver,
390 IDiagnostics* diag) {
391 CHECK(value != nullptr);
392 CHECK(diag != nullptr);
393
394 const Source& source = value->GetSource();
395 if (!ValidateName(name_validator, name, source, diag)) {
396 return false;
397 }
398
399 // Check for package names appearing twice with two different package ids
400 ResourceTablePackage* package = FindOrCreatePackage(name.package);
401 if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
402 diag->Error(DiagMessage(source)
403 << "trying to add resource '" << name << "' with ID " << res_id
404 << " but package '" << package->name << "' already has ID "
405 << StringPrintf("%02x", package->id.value()));
406 return false;
407 }
408
409 // Whether or not to error on duplicate resources
410 bool check_id = validate_resources_ && res_id.is_valid();
411 // Whether or not to create a duplicate resource if the id does not match
412 bool use_id = !validate_resources_ && res_id.is_valid();
413
414 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
415 : Maybe<uint8_t>());
416
417 // Check for types appearing twice with two different type ids
418 if (check_id && type->id && type->id.value() != res_id.type_id()) {
419 diag->Error(DiagMessage(source)
420 << "trying to add resource '" << name << "' with ID " << res_id
421 << " but type '" << type->type << "' already has ID "
422 << StringPrintf("%02x", type->id.value()));
423 return false;
424 }
425
426 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
427 : Maybe<uint16_t>());
428
429 // Check for entries appearing twice with two different entry ids
430 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
431 diag->Error(DiagMessage(source)
432 << "trying to add resource '" << name << "' with ID " << res_id
433 << " but resource already has ID "
434 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
435 return false;
436 }
437
438 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
439 if (!config_value->value) {
440 // Resource does not exist, add it now.
441 config_value->value = std::move(value);
442 } else {
443 switch (conflict_resolver(config_value->value.get(), value.get())) {
444 case CollisionResult::kKeepBoth:
445 // Insert the value ignoring for duplicate configurations
446 entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
447 entry->values.back()->value = std::move(value);
448 break;
449
450 case CollisionResult::kTakeNew:
451 // Take the incoming value.
452 config_value->value = std::move(value);
453 break;
454
455 case CollisionResult::kConflict:
456 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
457 << "with config '" << config << "'");
458 diag->Error(DiagMessage(source) << "resource previously defined here");
459 return false;
460
461 case CollisionResult::kKeepOriginal:
462 break;
463 }
464 }
465
466 if (res_id.is_valid()) {
467 package->id = res_id.package_id();
468 type->id = res_id.type_id();
469 entry->id = res_id.entry_id();
470 }
471
472 return true;
473 }
474
GetValidateResources()475 bool ResourceTable::GetValidateResources() {
476 return validate_resources_;
477 }
478
SetVisibility(const ResourceNameRef & name,const Visibility & visibility,IDiagnostics * diag)479 bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
480 IDiagnostics* diag) {
481 return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
482 }
483
SetVisibilityWithId(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,IDiagnostics * diag)484 bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
485 const ResourceId& res_id, IDiagnostics* diag) {
486 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
487 }
488
SetVisibilityWithIdMangled(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,IDiagnostics * diag)489 bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
490 const Visibility& visibility,
491 const ResourceId& res_id, IDiagnostics* diag) {
492 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
493 }
494
SetVisibilityImpl(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,NameValidator name_validator,IDiagnostics * diag)495 bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
496 const ResourceId& res_id, NameValidator name_validator,
497 IDiagnostics* diag) {
498 CHECK(diag != nullptr);
499
500 const Source& source = visibility.source;
501 if (!ValidateName(name_validator, name, source, diag)) {
502 return false;
503 }
504
505 // Check for package names appearing twice with two different package ids
506 ResourceTablePackage* package = FindOrCreatePackage(name.package);
507 if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
508 diag->Error(DiagMessage(source)
509 << "trying to add resource '" << name << "' with ID " << res_id
510 << " but package '" << package->name << "' already has ID "
511 << StringPrintf("%02x", package->id.value()));
512 return false;
513 }
514
515 // Whether or not to error on duplicate resources
516 bool check_id = validate_resources_ && res_id.is_valid();
517 // Whether or not to create a duplicate resource if the id does not match
518 bool use_id = !validate_resources_ && res_id.is_valid();
519
520 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
521 : Maybe<uint8_t>());
522
523 // Check for types appearing twice with two different type ids
524 if (check_id && type->id && type->id.value() != res_id.type_id()) {
525 diag->Error(DiagMessage(source)
526 << "trying to add resource '" << name << "' with ID " << res_id
527 << " but type '" << type->type << "' already has ID "
528 << StringPrintf("%02x", type->id.value()));
529 return false;
530 }
531
532 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
533 : Maybe<uint16_t>());
534
535 // Check for entries appearing twice with two different entry ids
536 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
537 diag->Error(DiagMessage(source)
538 << "trying to add resource '" << name << "' with ID " << res_id
539 << " but resource already has ID "
540 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
541 return false;
542 }
543
544 if (res_id.is_valid()) {
545 package->id = res_id.package_id();
546 type->id = res_id.type_id();
547 entry->id = res_id.entry_id();
548 }
549
550 // Only mark the type visibility level as public, it doesn't care about being private.
551 if (visibility.level == Visibility::Level::kPublic) {
552 type->visibility_level = Visibility::Level::kPublic;
553 }
554
555 if (visibility.level == Visibility::Level::kUndefined &&
556 entry->visibility.level != Visibility::Level::kUndefined) {
557 // We can't undefine a symbol (remove its visibility). Ignore.
558 return true;
559 }
560
561 if (visibility.level < entry->visibility.level) {
562 // We can't downgrade public to private. Ignore.
563 return true;
564 }
565
566 // This symbol definition takes precedence, replace.
567 entry->visibility = visibility;
568 return true;
569 }
570
SetAllowNew(const ResourceNameRef & name,const AllowNew & allow_new,IDiagnostics * diag)571 bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
572 IDiagnostics* diag) {
573 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
574 }
575
SetAllowNewMangled(const ResourceNameRef & name,const AllowNew & allow_new,IDiagnostics * diag)576 bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
577 IDiagnostics* diag) {
578 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
579 }
580
SetAllowNewImpl(const ResourceNameRef & name,const AllowNew & allow_new,NameValidator name_validator,IDiagnostics * diag)581 bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
582 NameValidator name_validator, IDiagnostics* diag) {
583 CHECK(diag != nullptr);
584
585 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
586 return false;
587 }
588
589 ResourceTablePackage* package = FindOrCreatePackage(name.package);
590 ResourceTableType* type = package->FindOrCreateType(name.type);
591 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
592 entry->allow_new = allow_new;
593 return true;
594 }
595
SetOverlayable(const ResourceNameRef & name,const OverlayableItem & overlayable,IDiagnostics * diag)596 bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
597 IDiagnostics* diag) {
598 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
599 }
600
SetOverlayableImpl(const ResourceNameRef & name,const OverlayableItem & overlayable,NameValidator name_validator,IDiagnostics * diag)601 bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
602 const OverlayableItem& overlayable,
603 NameValidator name_validator, IDiagnostics *diag) {
604 CHECK(diag != nullptr);
605
606 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
607 return false;
608 }
609
610 ResourceTablePackage* package = FindOrCreatePackage(name.package);
611 ResourceTableType* type = package->FindOrCreateType(name.type);
612 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
613
614 if (entry->overlayable_item) {
615 diag->Error(DiagMessage(overlayable.source)
616 << "duplicate overlayable declaration for resource '" << name << "'");
617 diag->Error(DiagMessage(entry->overlayable_item.value().source)
618 << "previous declaration here");
619 return false;
620 }
621
622 entry->overlayable_item = overlayable;
623 return true;
624 }
625
FindResource(const ResourceNameRef & name) const626 Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
627 ResourceTablePackage* package = FindPackage(name.package);
628 if (package == nullptr) {
629 return {};
630 }
631
632 ResourceTableType* type = package->FindType(name.type);
633 if (type == nullptr) {
634 return {};
635 }
636
637 ResourceEntry* entry = type->FindEntry(name.entry);
638 if (entry == nullptr) {
639 return {};
640 }
641 return SearchResult{package, type, entry};
642 }
643
Clone() const644 std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
645 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
646 for (const auto& pkg : packages) {
647 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
648 for (const auto& type : pkg->types) {
649 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
650 new_type->id = type->id;
651 new_type->visibility_level = type->visibility_level;
652
653 for (const auto& entry : type->entries) {
654 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
655 new_entry->id = entry->id;
656 new_entry->visibility = entry->visibility;
657 new_entry->allow_new = entry->allow_new;
658 new_entry->overlayable_item = entry->overlayable_item;
659
660 for (const auto& config_value : entry->values) {
661 ResourceConfigValue* new_value =
662 new_entry->FindOrCreateValue(config_value->config, config_value->product);
663 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
664 }
665 }
666 }
667 }
668 return new_table;
669 }
670
671 } // namespace aapt
672