blob: 0304e21698df1c38fc3bdaa2a80989e9c1f66772 [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"
25#include "androidfw/ResourceTypes.h"
26
27#include "ConfigDescription.h"
28#include "NameMangler.h"
29#include "ResourceValues.h"
30#include "ValueVisitor.h"
31#include "text/Unicode.h"
32#include "util/Util.h"
33
34using ::aapt::text::IsValidResourceEntryName;
35using ::android::StringPiece;
Adam Lesinskid5083f62017-01-16 15:07:21 -080036
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080037namespace aapt {
38
Adam Lesinskib1afa072017-03-29 13:52:38 -070039static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070040 return lhs->type < rhs;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080041}
42
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043template <typename T>
Adam Lesinskib1afa072017-03-29 13:52:38 -070044static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070045 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080046}
47
Adam Lesinskice5e56e2016-10-21 17:56:45 -070048ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070050 auto iter = std::lower_bound(packages.begin(), last, name,
51 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 if (iter != last && name == (*iter)->name) {
53 return iter->get();
54 }
55 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080056}
57
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 for (auto& package : packages) {
60 if (package->id && package->id.value() == id) {
61 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080062 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070063 }
64 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065}
66
Adam Lesinskib1afa072017-03-29 13:52:38 -070067ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070069 if (id && !package->id) {
70 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070071 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072 }
73
74 if (id && package->id && package->id.value() != id.value()) {
75 return nullptr;
76 }
77 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080078}
79
Adam Lesinskib1afa072017-03-29 13:52:38 -070080ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070081 const auto last = packages.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -070082 auto iter = std::lower_bound(packages.begin(), last, name,
83 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 if (iter != last && name == (*iter)->name) {
85 return iter->get();
86 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080087
Adam Lesinskib1afa072017-03-29 13:52:38 -070088 std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
Adam Lesinskid5083f62017-01-16 15:07:21 -080089 new_package->name = name.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -070091}
92
Adam Lesinskice5e56e2016-10-21 17:56:45 -070093ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070096 if (iter != last && (*iter)->type == type) {
97 return iter->get();
98 }
99 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700100}
101
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700103 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700104 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 if (iter != last && (*iter)->type == type) {
106 return iter->get();
107 }
108 return types.emplace(iter, new ResourceTableType(type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700109}
110
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700111ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700112 const auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700113 auto iter =
114 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 if (iter != last && name == (*iter)->name) {
116 return iter->get();
117 }
118 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700119}
120
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700121ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700122 auto last = entries.end();
Adam Lesinskib1afa072017-03-29 13:52:38 -0700123 auto iter =
124 std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 if (iter != last && name == (*iter)->name) {
126 return iter->get();
127 }
128 return entries.emplace(iter, new ResourceEntry(name))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700129}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800130
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700131ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
132 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800133}
134
135struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 const ConfigDescription* config;
137 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800138};
139
Adam Lesinskib1afa072017-03-29 13:52:38 -0700140bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 int cmp = lhs->config.compare(*rhs.config);
142 if (cmp == 0) {
143 cmp = StringPiece(lhs->product).compare(rhs.product);
144 }
145 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800146}
147
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700148ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800149 const StringPiece& product) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700150 auto iter =
151 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152 if (iter != values.end()) {
153 ResourceConfigValue* value = iter->get();
154 if (value->config == config && StringPiece(value->product) == product) {
155 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800156 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 }
158 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800159}
160
Adam Lesinskib1afa072017-03-29 13:52:38 -0700161ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
162 const StringPiece& product) {
163 auto iter =
164 std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 if (iter != values.end()) {
166 ResourceConfigValue* value = iter->get();
167 if (value->config == config && StringPiece(value->product) == product) {
168 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800169 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700170 }
171 ResourceConfigValue* newValue =
Adam Lesinskib1afa072017-03-29 13:52:38 -0700172 values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700173 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800174}
175
Adam Lesinskib1afa072017-03-29 13:52:38 -0700176std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700177 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800178
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700179 auto iter = values.begin();
180 for (; iter != values.end(); ++iter) {
181 ResourceConfigValue* value = iter->get();
182 if (value->config == config) {
183 results.push_back(value);
184 ++iter;
185 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800186 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700187 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800188
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700189 for (; iter != values.end(); ++iter) {
190 ResourceConfigValue* value = iter->get();
191 if (value->config == config) {
192 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800193 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 }
195 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800196}
197
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700198std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 const std::function<bool(ResourceConfigValue*)>& f) {
200 std::vector<ResourceConfigValue*> results;
201 for (auto& configValue : values) {
202 if (f(configValue.get())) {
203 results.push_back(configValue.get());
Adam Lesinski458b8772016-04-25 14:20:21 -0700204 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700205 }
206 return results;
Adam Lesinski458b8772016-04-25 14:20:21 -0700207}
208
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800209/**
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700210 * The default handler for collisions.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700211 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 * Typically, a weak value will be overridden by a strong value. An existing
213 * weak
Adam Lesinski8197cc462016-08-19 12:16:49 -0700214 * 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 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 * USE is anywhere an Attribute is declared without a format, and in a place
221 * that would
Adam Lesinski8197cc462016-08-19 12:16:49 -0700222 * be legal to declare if the Attribute already existed. This is typically in a
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700223 * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
224 * weak.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700225 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700226 * DECL is an absolute declaration of an Attribute and specifies an explicit
227 * format.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700228 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700229 * A DECL will override a USE without error. Two DECLs must match in their
230 * format for there to be
Adam Lesinski8197cc462016-08-19 12:16:49 -0700231 * no error.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800232 */
Adam Lesinskib1afa072017-03-29 13:52:38 -0700233ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
234 Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700235 Attribute* existing_attr = ValueCast<Attribute>(existing);
236 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
237 if (!incoming_attr) {
238 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700239 // We're trying to add a weak resource but a resource
240 // already exists. Keep the existing.
241 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700242 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700243 // Override the weak resource with the new strong resource.
244 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800245 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700246 // The existing and incoming values are strong, this is an error
247 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700248 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700249 }
250
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700251 if (!existing_attr) {
252 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700253 // The existing value is not an attribute and it is weak,
254 // so take the incoming attribute value.
255 return CollisionResult::kTakeNew;
256 }
257 // The existing value is not an attribute and it is strong,
258 // so the incoming attribute value is an error.
259 return CollisionResult::kConflict;
260 }
261
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700262 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700263
264 //
265 // Attribute specific handling. At this point we know both
266 // values are attributes. Since we can declare and define
267 // attributes all-over, we do special handling to see
268 // which definition sticks.
269 //
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700270 if (existing_attr->type_mask == incoming_attr->type_mask) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 // The two attributes are both DECLs, but they are plain attributes
272 // with the same formats.
273 // Keep the strongest one.
Adam Lesinskib1afa072017-03-29 13:52:38 -0700274 return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700275 }
276
Adam Lesinskib1afa072017-03-29 13:52:38 -0700277 if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700278 // Any incoming attribute is better than this.
279 return CollisionResult::kTakeNew;
280 }
281
Adam Lesinskib1afa072017-03-29 13:52:38 -0700282 if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700283 // The incoming attribute may be a USE instead of a DECL.
284 // Keep the existing attribute.
285 return CollisionResult::kKeepOriginal;
286 }
287 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800288}
289
Adam Lesinskib1afa072017-03-29 13:52:38 -0700290static StringPiece ValidateName(const StringPiece& name) {
Adam Lesinski66ea8402017-06-28 11:44:11 -0700291 if (!IsValidResourceEntryName(name)) {
292 return name;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700293 }
294 return {};
295}
296
297static StringPiece SkipValidateName(const StringPiece& /*name*/) {
298 return {};
299}
Adam Lesinski330edcd2015-05-04 17:40:56 -0700300
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700301bool ResourceTable::AddResource(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800302 const ConfigDescription& config,
303 const StringPiece& product,
304 std::unique_ptr<Value> value,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700305 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700306 return AddResourceImpl(name, {}, config, product, std::move(value), ValidateName,
307 ResolveValueCollision, diag);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700308}
309
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700310bool ResourceTable::AddResource(const ResourceNameRef& name,
311 const ResourceId& res_id,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800312 const ConfigDescription& config,
313 const StringPiece& product,
314 std::unique_ptr<Value> value,
315 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700316 return AddResourceImpl(name, res_id, config, product, std::move(value), ValidateName,
317 ResolveValueCollision, diag);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800318}
319
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700320bool ResourceTable::AddFileReference(const ResourceNameRef& name,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800321 const ConfigDescription& config,
322 const Source& source,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700323 const StringPiece& path,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700324 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700325 return AddFileReferenceImpl(name, config, source, path, nullptr, ValidateName, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800326}
327
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700328bool ResourceTable::AddFileReferenceAllowMangled(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700329 const ResourceNameRef& name, const ConfigDescription& config,
330 const Source& source, const StringPiece& path, io::IFile* file,
331 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700332 return AddFileReferenceImpl(name, config, source, path, file, SkipValidateName, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800333}
334
Adam Lesinskib1afa072017-03-29 13:52:38 -0700335bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
336 const ConfigDescription& config, const Source& source,
337 const StringPiece& path, io::IFile* file,
338 NameValidator name_validator, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700339 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700340 util::make_unique<FileReference>(string_pool.MakeRef(path));
341 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700342 fileRef->file = file;
Adam Lesinskib1afa072017-03-29 13:52:38 -0700343 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
344 name_validator, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700345}
346
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700347bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
Adam Lesinski330edcd2015-05-04 17:40:56 -0700348 const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800349 const StringPiece& product,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700350 std::unique_ptr<Value> value,
351 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700352 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipValidateName,
353 ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700354}
355
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700356bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700357 const ResourceId& id,
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700358 const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800359 const StringPiece& product,
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700360 std::unique_ptr<Value> value,
361 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700362 return AddResourceImpl(name, id, config, product, std::move(value), SkipValidateName,
363 ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700364}
365
Adam Lesinskib1afa072017-03-29 13:52:38 -0700366bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
367 const ConfigDescription& config, const StringPiece& product,
368 std::unique_ptr<Value> value, NameValidator name_validator,
369 const CollisionResolverFunc& conflictResolver,
370 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700371 CHECK(value != nullptr);
372 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700373
Adam Lesinskib1afa072017-03-29 13:52:38 -0700374 const StringPiece bad_char = name_validator(name.entry);
375 if (!bad_char.empty()) {
376 diag->Error(DiagMessage(value->GetSource()) << "resource '" << name
377 << "' has invalid entry name '" << name.entry
378 << "'. Invalid character '" << bad_char << "'");
379
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700380 return false;
381 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800382
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700383 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800384 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700385 diag->Error(DiagMessage(value->GetSource())
386 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700387 << " but package '" << package->name << "' already has ID "
388 << std::hex << (int)package->id.value() << std::dec);
389 return false;
390 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800391
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700392 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800393 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700394 diag->Error(DiagMessage(value->GetSource())
395 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700396 << " but type '" << type->type << "' already has ID "
397 << std::hex << (int)type->id.value() << std::dec);
398 return false;
399 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800400
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700401 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800402 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700403 diag->Error(DiagMessage(value->GetSource())
404 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700405 << " but resource already has ID "
406 << ResourceId(package->id.value(), type->id.value(),
407 entry->id.value()));
408 return false;
409 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700410
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700411 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
412 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700413 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700414 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700415
416 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700417 switch (conflictResolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700418 case CollisionResult::kTakeNew:
419 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700420 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700421 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800422
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 case CollisionResult::kConflict:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700424 diag->Error(DiagMessage(value->GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700425 << "duplicate value for resource '" << name << "' "
426 << "with config '" << config << "'");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700427 diag->Error(DiagMessage(config_value->value->GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700428 << "resource previously defined here");
429 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700430
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 case CollisionResult::kKeepOriginal:
432 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800433 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700434 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800435
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800436 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700437 package->id = res_id.package_id();
438 type->id = res_id.type_id();
439 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700440 }
441 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800442}
443
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700444bool ResourceTable::SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700445 const Symbol& symbol, IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700446 return SetSymbolStateImpl(name, res_id, symbol, ValidateName, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700447}
448
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700449bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
450 const ResourceId& res_id,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700451 const Symbol& symbol,
452 IDiagnostics* diag) {
Adam Lesinskib1afa072017-03-29 13:52:38 -0700453 return SetSymbolStateImpl(name, res_id, symbol, SkipValidateName, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700454}
455
Adam Lesinskib1afa072017-03-29 13:52:38 -0700456bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
457 const Symbol& symbol, NameValidator name_validator,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700458 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700459 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700460
Adam Lesinskib1afa072017-03-29 13:52:38 -0700461 const StringPiece bad_char = name_validator(name.entry);
462 if (!bad_char.empty()) {
463 diag->Error(DiagMessage(symbol.source) << "resource '" << name << "' has invalid entry name '"
464 << name.entry << "'. Invalid character '" << bad_char
465 << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700466 return false;
467 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800468
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700469 ResourceTablePackage* package = FindOrCreatePackage(name.package);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800470 if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700471 diag->Error(DiagMessage(symbol.source)
472 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700473 << " but package '" << package->name << "' already has ID "
474 << std::hex << (int)package->id.value() << std::dec);
475 return false;
476 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800477
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700478 ResourceTableType* type = package->FindOrCreateType(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800479 if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700480 diag->Error(DiagMessage(symbol.source)
481 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700482 << " but type '" << type->type << "' already has ID "
483 << std::hex << (int)type->id.value() << std::dec);
484 return false;
485 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700486
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700487 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800488 if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700489 diag->Error(DiagMessage(symbol.source)
490 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700491 << " but resource already has ID "
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700492 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700493 return false;
494 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800495
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800496 if (res_id.is_valid_dynamic()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700497 package->id = res_id.package_id();
498 type->id = res_id.type_id();
499 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700500 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800501
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700502 // Only mark the type state as public, it doesn't care about being private.
503 if (symbol.state == SymbolState::kPublic) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700504 type->symbol_status.state = SymbolState::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700505 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800506
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700507 if (symbol.allow_new) {
508 // This symbol can be added as a new resource when merging (if it belongs to an overlay).
509 entry->symbol_status.allow_new = true;
510 }
511
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700512 if (symbol.state == SymbolState::kUndefined &&
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700513 entry->symbol_status.state != SymbolState::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700514 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800515 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700516 }
517
518 if (symbol.state == SymbolState::kPrivate &&
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700519 entry->symbol_status.state == SymbolState::kPublic) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700520 // We can't downgrade public to private. Ignore.
521 return true;
522 }
523
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700524 // This symbol definition takes precedence, replace.
525 entry->symbol_status.state = symbol.state;
526 entry->symbol_status.source = symbol.source;
527 entry->symbol_status.comment = symbol.comment;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700528 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800529}
530
Adam Lesinskib1afa072017-03-29 13:52:38 -0700531Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700532 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700533 if (!package) {
534 return {};
535 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800536
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700537 ResourceTableType* type = package->FindType(name.type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700538 if (!type) {
539 return {};
540 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800541
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700542 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700543 if (!entry) {
544 return {};
545 }
546 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800547}
548
Shane Farmer0a5b2012017-06-22 12:24:12 -0700549std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
550 std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
551 for (const auto& pkg : packages) {
552 ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
553 for (const auto& type : pkg->types) {
554 ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
555 if (!new_type->id) {
556 new_type->id = type->id;
557 new_type->symbol_status = type->symbol_status;
558 }
559
560 for (const auto& entry : type->entries) {
561 ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
562 if (!new_entry->id) {
563 new_entry->id = entry->id;
564 new_entry->symbol_status = entry->symbol_status;
565 }
566
567 for (const auto& config_value : entry->values) {
568 ResourceConfigValue* new_value =
569 new_entry->FindOrCreateValue(config_value->config, config_value->product);
570 Value* value = config_value->value->Clone(&new_table->string_pool);
571 new_value->value = std::unique_ptr<Value>(value);
572 }
573 }
574 }
575 }
576 return new_table;
577}
578
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700579} // namespace aapt