blob: 54633ad5c5e32f9d90ba9af69163560ec0dfef28 [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"
33#include "text/Unicode.h"
34#include "util/Util.h"
35
36using ::aapt::text::IsValidResourceEntryName;
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020037using ::android::ConfigDescription;
Adam Lesinski66ea8402017-06-28 11:44:11 -070038using ::android::StringPiece;
Adam Lesinski71be7052017-12-12 16:48:07 -080039using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080040
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080041namespace aapt {
42
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070043static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
44 const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
45 return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080046}
47
Adam Lesinski1ab598f2015-08-14 14:26:04 -070048template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070049static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070050 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080051}
52
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000053template <typename T>
54static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -070055 const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000056 int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -070057 return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000058}
59
Adam Lesinski71be7052017-12-12 16:48:07 -080060ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070062 auto iter = std::lower_bound(packages.begin(), last, name,
63 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070064 if (iter != last && name == (*iter)->name) {
65 return iter->get();
66 }
67 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080068}
69
Adam Lesinski71be7052017-12-12 16:48:07 -080070ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070071 for (auto& package : packages) {
72 if (package->id && package->id.value() == id) {
73 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080074 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070075 }
76 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077}
78
Adam Lesinskib1afa072017-03-29 13:52:38 -070079ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070080 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070081 if (id && !package->id) {
82 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070083 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 }
85
86 if (id && package->id && package->id.value() != id.value()) {
87 return nullptr;
88 }
89 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080090}
91
David Chaloupkae3c1a4a2018-01-18 13:44:36 +000092ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
93 const Maybe<uint8_t> id) {
94 const auto last = packages.end();
95 auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
96 less_than_struct_with_name_and_id<ResourceTablePackage>);
97
98 if (iter != last && name == (*iter)->name && id == (*iter)->id) {
99 return iter->get();
100 }
101
102 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
103 new_package->name = name.to_string();
104 new_package->id = id;
105 return packages.emplace(iter, std::move(new_package))->get();
106}
107
Adam Lesinskib1afa072017-03-29 13:52:38 -0700108ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700109 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700110 auto iter = std::lower_bound(packages.begin(), last, name,
111 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700112 if (iter != last && name == (*iter)->name) {
113 return iter->get();
114 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800115
Adam Lesinskib1afa072017-03-29 13:52:38 -0700116 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -0800117 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700118 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700119}
120
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700121ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700122 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700123 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
124 less_than_type_and_id);
125 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 return iter->get();
127 }
128 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700129}
130
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700131ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
132 const Maybe<uint8_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700133 const auto last = types.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700134 auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
135 less_than_type_and_id);
136 if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700137 return iter->get();
138 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700139
140 auto new_type = new ResourceTableType(type);
141 new_type->id = id;
142 return types.emplace(iter, std::move(new_type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700143}
144
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700145ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700146 const auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700147 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
148 less_than_struct_with_name_and_id<ResourceEntry>);
149 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 return iter->get();
151 }
152 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700153}
154
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700155ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700156 const Maybe<uint16_t > id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 auto last = entries.end();
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700158 auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
159 less_than_struct_with_name_and_id<ResourceEntry>);
160 if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 return iter->get();
162 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700163
164 auto new_entry = new ResourceEntry(name);
165 new_entry->id = id;
166 return entries.emplace(iter, std::move(new_entry))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700167}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800168
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700169ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
170 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800171}
172
173struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 const ConfigDescription* config;
175 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800176};
177
Adam Lesinski34a16872018-02-23 16:18:10 -0800178bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700179 int cmp = lhs->config.compare(*rhs.config);
180 if (cmp == 0) {
181 cmp = StringPiece(lhs->product).compare(rhs.product);
182 }
183 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800184}
185
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800187 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800188 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
189 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190 if (iter != values.end()) {
191 ResourceConfigValue* value = iter->get();
192 if (value->config == config && StringPiece(value->product) == product) {
193 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800194 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700195 }
196 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800197}
198
Adam Lesinskib1afa072017-03-29 13:52:38 -0700199ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
200 const StringPiece& product) {
Adam Lesinski34a16872018-02-23 16:18:10 -0800201 auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
202 lt_config_key_ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700203 if (iter != values.end()) {
204 ResourceConfigValue* value = iter->get();
205 if (value->config == config && StringPiece(value->product) == product) {
206 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800207 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700208 }
209 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700210 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700211 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800212}
213
Adam Lesinskib1afa072017-03-29 13:52:38 -0700214std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700215 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800216
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700217 auto iter = values.begin();
218 for (; iter != values.end(); ++iter) {
219 ResourceConfigValue* value = iter->get();
220 if (value->config == config) {
221 results.push_back(value);
222 ++iter;
223 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800224 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700225 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800226
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700227 for (; iter != values.end(); ++iter) {
228 ResourceConfigValue* value = iter->get();
229 if (value->config == config) {
230 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800231 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700232 }
233 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800234}
235
Adam Lesinski34a16872018-02-23 16:18:10 -0800236bool ResourceEntry::HasDefaultValue() const {
237 const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
238
239 // The default config should be at the top of the list, since the list is sorted.
240 for (auto& config_value : values) {
241 if (config_value->config == default_config) {
242 return true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700243 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700244 }
Adam Lesinski34a16872018-02-23 16:18:10 -0800245 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700246}
247
Adam Lesinski71be7052017-12-12 16:48:07 -0800248// The default handler for collisions.
249//
250// Typically, a weak value will be overridden by a strong value. An existing weak
251// value will not be overridden by an incoming weak value.
252//
253// There are some exceptions:
254//
255// Attributes: There are two types of Attribute values: USE and DECL.
256//
257// USE is anywhere an Attribute is declared without a format, and in a place that would
258// be legal to declare if the Attribute already existed. This is typically in a
259// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
260//
261// DECL is an absolute declaration of an Attribute and specifies an explicit format.
262//
263// A DECL will override a USE without error. Two DECLs must match in their format for there to be
264// no error.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700265ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
266 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700267 Attribute* existing_attr = ValueCast<Attribute>(existing);
268 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
269 if (!incoming_attr) {
270 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 // We're trying to add a weak resource but a resource
272 // already exists. Keep the existing.
273 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700274 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700275 // Override the weak resource with the new strong resource.
276 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800277 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700278 // The existing and incoming values are strong, this is an error
279 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700280 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700281 }
282
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700283 if (!existing_attr) {
284 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700285 // The existing value is not an attribute and it is weak,
286 // so take the incoming attribute value.
287 return CollisionResult::kTakeNew;
288 }
289 // The existing value is not an attribute and it is strong,
290 // so the incoming attribute value is an error.
291 return CollisionResult::kConflict;
292 }
293
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700294 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700295
296 //
297 // Attribute specific handling. At this point we know both
298 // values are attributes. Since we can declare and define
299 // attributes all-over, we do special handling to see
300 // which definition sticks.
301 //
Adam Lesinski73bff1e2017-12-08 16:06:10 -0800302 if (existing_attr->IsCompatibleWith(*incoming_attr)) {
303 // The two attributes are both DECLs, but they are plain attributes with compatible formats.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700304 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700305 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700306 }
307
Adam Lesinskib1afa072017-03-29 13:52:38 -0700308 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700309 // Any incoming attribute is better than this.
310 return CollisionResult::kTakeNew;
311 }
312
Adam Lesinskib1afa072017-03-29 13:52:38 -0700313 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700314 // The incoming attribute may be a USE instead of a DECL.
315 // Keep the existing attribute.
316 return CollisionResult::kKeepOriginal;
317 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700318
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800320}
321
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700322ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
323 Value* /** incoming **/) {
324 return CollisionResult::kKeepBoth;
325}
326
Adam Lesinski71be7052017-12-12 16:48:07 -0800327static StringPiece ResourceNameValidator(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700328 if (!IsValidResourceEntryName(name)) {
329 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700330 }
331 return {};
332}
333
Adam Lesinski71be7052017-12-12 16:48:07 -0800334static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700335 return {};
336}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700337
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700338bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800339 const ConfigDescription& config,
340 const StringPiece& product,
341 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700342 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700343 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
344 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
345 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700346}
347
Adam Lesinski71be7052017-12-12 16:48:07 -0800348bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
349 const ConfigDescription& config, const StringPiece& product,
350 std::unique_ptr<Value> value, IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700351 return AddResourceImpl(name, res_id, config, product, std::move(value),
352 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
353 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800354}
355
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700356bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800357 const ConfigDescription& config,
358 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700359 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700360 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700361 return AddFileReferenceImpl(name, config, source, path, nullptr,
362 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
363 diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800364}
365
Adam Lesinski71be7052017-12-12 16:48:07 -0800366bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
367 const ConfigDescription& config, const Source& source,
368 const StringPiece& path, io::IFile* file,
369 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700370 return AddFileReferenceImpl(name, config, source, path, file,
371 (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
372 diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800373}
374
Adam Lesinskib1afa072017-03-29 13:52:38 -0700375bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
376 const ConfigDescription& config, const Source& source,
377 const StringPiece& path, io::IFile* file,
378 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700379 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700380 util::make_unique<FileReference>(string_pool.MakeRef(path));
381 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700382 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700383 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
384 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700385}
386
Adam Lesinski71be7052017-12-12 16:48:07 -0800387bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
388 const StringPiece& product, std::unique_ptr<Value> value,
389 IDiagnostics* diag) {
390 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700391 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700392}
393
Adam Lesinski71be7052017-12-12 16:48:07 -0800394bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
395 const ConfigDescription& config,
396 const StringPiece& product,
397 std::unique_ptr<Value> value, IDiagnostics* diag) {
398 return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700399 (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700400}
401
Adam Lesinski71be7052017-12-12 16:48:07 -0800402bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
403 const Source& source, IDiagnostics* diag) {
404 const StringPiece bad_char = name_validator(name.entry);
405 if (!bad_char.empty()) {
406 diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
407 << name.entry << "'. Invalid character '" << bad_char << "'");
408 return false;
409 }
410 return true;
411}
412
Adam Lesinskib1afa072017-03-29 13:52:38 -0700413bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
414 const ConfigDescription& config, const StringPiece& product,
415 std::unique_ptr<Value> value, NameValidator name_validator,
Adam Lesinski71be7052017-12-12 16:48:07 -0800416 const CollisionResolverFunc& conflict_resolver,
Adam Lesinskib1afa072017-03-29 13:52:38 -0700417 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700418 CHECK(value != nullptr);
419 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700420
Adam Lesinski71be7052017-12-12 16:48:07 -0800421 const Source& source = value->GetSource();
422 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 return false;
424 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800425
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700426 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700427 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800428 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700429 diag->Error(DiagMessage(source)
430 << "trying to add resource '" << name << "' with ID " << res_id
431 << " but package '" << package->name << "' already has ID "
432 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700433 return false;
434 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800435
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700436 // Whether or not to error on duplicate resources
437 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
438 // Whether or not to create a duplicate resource if the id does not match
439 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
440
441 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
442 : Maybe<uint8_t>());
443
444 // Check for types appearing twice with two different type ids
445 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800446 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700447 << "trying to add resource '" << name << "' with ID " << res_id
448 << " but type '" << type->type << "' already has ID "
449 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700450 return false;
451 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800452
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700453 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700454 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700455
456 // Check for entries appearing twice with two different entry ids
457 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800458 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700459 << "trying to add resource '" << name << "' with ID " << res_id
460 << " but resource already has ID "
461 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700462 return false;
463 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700464
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700465 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700466 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700467 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700468 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700469 } else {
Adam Lesinski71be7052017-12-12 16:48:07 -0800470 switch (conflict_resolver(config_value->value.get(), value.get())) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700471 case CollisionResult::kKeepBoth:
472 // Insert the value ignoring for duplicate configurations
473 entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
474 entry->values.back()->value = std::move(value);
475 break;
476
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700477 case CollisionResult::kTakeNew:
478 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700479 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700480 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800481
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700482 case CollisionResult::kConflict:
Adam Lesinski71be7052017-12-12 16:48:07 -0800483 diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
484 << "with config '" << config << "'");
485 diag->Error(DiagMessage(source) << "resource previously defined here");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700486 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700487
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700488 case CollisionResult::kKeepOriginal:
489 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800490 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700491 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800492
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800493 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700494 package->id = res_id.package_id();
495 type->id = res_id.type_id();
496 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700497 }
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700498
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700499 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800500}
501
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700502bool ResourceTable::GetValidateResources() {
503 return validate_resources_;
504}
505
Adam Lesinski71be7052017-12-12 16:48:07 -0800506bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
507 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700508 return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700509}
510
Adam Lesinski71be7052017-12-12 16:48:07 -0800511bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
512 IDiagnostics* diag) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700513 return SetVisibilityImpl(name, visibility, {}, SkipNameValidator, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700514}
515
Adam Lesinski71be7052017-12-12 16:48:07 -0800516bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
517 const ResourceId& res_id, IDiagnostics* diag) {
518 return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
519}
520
521bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
522 const Visibility& visibility,
523 const ResourceId& res_id, IDiagnostics* diag) {
524 return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
525}
526
527bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
528 const ResourceId& res_id, NameValidator name_validator,
529 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700530 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700531
Adam Lesinski71be7052017-12-12 16:48:07 -0800532 const Source& source = visibility.source;
533 if (!ValidateName(name_validator, name, source, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700534 return false;
535 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800536
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700537 // Check for package names appearing twice with two different package ids
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700538 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800539 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700540 diag->Error(DiagMessage(source)
541 << "trying to add resource '" << name << "' with ID " << res_id
542 << " but package '" << package->name << "' already has ID "
543 << StringPrintf("%02x", package->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700544 return false;
545 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800546
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700547 // Whether or not to error on duplicate resources
548 bool check_id = validate_resources_ && res_id.is_valid_dynamic();
549 // Whether or not to create a duplicate resource if the id does not match
550 bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
551
552 ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
553 : Maybe<uint8_t>());
554
555 // Check for types appearing twice with two different type ids
556 if (check_id && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800557 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700558 << "trying to add resource '" << name << "' with ID " << res_id
559 << " but type '" << type->type << "' already has ID "
560 << StringPrintf("%02x", type->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700561 return false;
562 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700563
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700564 ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
Ryan Mitchell8d4ee972018-08-27 11:24:04 -0700565 : Maybe<uint16_t>());
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700566
567 // Check for entries appearing twice with two different entry ids
568 if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800569 diag->Error(DiagMessage(source)
Ryan Mitchell83a37ad2018-08-06 16:32:24 -0700570 << "trying to add resource '" << name << "' with ID " << res_id
571 << " but resource already has ID "
572 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700573 return false;
574 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800575
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800576 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700577 package->id = res_id.package_id();
578 type->id = res_id.type_id();
579 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700580 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800581
Adam Lesinski71be7052017-12-12 16:48:07 -0800582 // Only mark the type visibility level as public, it doesn't care about being private.
583 if (visibility.level == Visibility::Level::kPublic) {
584 type->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700585 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800586
Adam Lesinski71be7052017-12-12 16:48:07 -0800587 if (visibility.level == Visibility::Level::kUndefined &&
588 entry->visibility.level != Visibility::Level::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700589 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800590 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700591 }
592
Adam Lesinski71be7052017-12-12 16:48:07 -0800593 if (visibility.level < entry->visibility.level) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700594 // We can't downgrade public to private. Ignore.
595 return true;
596 }
597
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700598 // This symbol definition takes precedence, replace.
Adam Lesinski71be7052017-12-12 16:48:07 -0800599 entry->visibility = visibility;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700600 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800601}
602
Adam Lesinski71be7052017-12-12 16:48:07 -0800603bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
604 IDiagnostics* diag) {
605 return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
606}
607
608bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
609 IDiagnostics* diag) {
610 return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
611}
612
613bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
614 NameValidator name_validator, IDiagnostics* diag) {
615 CHECK(diag != nullptr);
616
617 if (!ValidateName(name_validator, name, allow_new.source, diag)) {
618 return false;
619 }
620
621 ResourceTablePackage* package = FindOrCreatePackage(name.package);
622 ResourceTableType* type = package->FindOrCreateType(name.type);
623 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
624 entry->allow_new = allow_new;
625 return true;
626}
627
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800628bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
Adam Lesinski71be7052017-12-12 16:48:07 -0800629 IDiagnostics* diag) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800630 return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
Adam Lesinski71be7052017-12-12 16:48:07 -0800631}
632
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800633bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
Adam Lesinski71be7052017-12-12 16:48:07 -0800634 const Overlayable& overlayable, IDiagnostics* diag) {
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800635 return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
Adam Lesinski71be7052017-12-12 16:48:07 -0800636}
637
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800638bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
639 NameValidator name_validator, IDiagnostics *diag) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800640 CHECK(diag != nullptr);
641
642 if (!ValidateName(name_validator, name, overlayable.source, diag)) {
643 return false;
644 }
645
646 ResourceTablePackage* package = FindOrCreatePackage(name.package);
647 ResourceTableType* type = package->FindOrCreateType(name.type);
648 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700649
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800650 if (entry->overlayable) {
651 diag->Error(DiagMessage(overlayable.source)
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700652 << "duplicate overlayable declaration for resource '" << name << "'");
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800653 diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
654 return false;
Adam Lesinski71be7052017-12-12 16:48:07 -0800655 }
Ryan Mitchelle4e989c2018-10-29 02:21:50 -0700656
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800657 entry->overlayable = overlayable;
Adam Lesinski71be7052017-12-12 16:48:07 -0800658 return true;
659}
660
661Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700662 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinski71be7052017-12-12 16:48:07 -0800663 if (package == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700664 return {};
665 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800666
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700667 ResourceTableType* type = package->FindType(name.type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800668 if (type == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700669 return {};
670 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800671
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700672 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinski71be7052017-12-12 16:48:07 -0800673 if (entry == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700674 return {};
675 }
676 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800677}
678
Shane Farmer0a5b2012017-06-22 12:24:12 -0700679std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
680 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
681 for (const auto& pkg : packages) {
682 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
683 for (const auto& type : pkg->types) {
684 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
Adam Lesinski71be7052017-12-12 16:48:07 -0800685 new_type->id = type->id;
686 new_type->visibility_level = type->visibility_level;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700687
688 for (const auto& entry : type->entries) {
689 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
Adam Lesinski71be7052017-12-12 16:48:07 -0800690 new_entry->id = entry->id;
691 new_entry->visibility = entry->visibility;
692 new_entry->allow_new = entry->allow_new;
Ryan Mitchell1bb1fe02018-11-16 11:21:41 -0800693 new_entry->overlayable = entry->overlayable;
Shane Farmer0a5b2012017-06-22 12:24:12 -0700694
695 for (const auto& config_value : entry->values) {
696 ResourceConfigValue* new_value =
697 new_entry->FindOrCreateValue(config_value->config, config_value->product);
Adam Lesinski71be7052017-12-12 16:48:07 -0800698 new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700699 }
700 }
701 }
702 }
703 return new_table;
704}
705
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700706} // namespace aapt