blob: 4e6a50ae149b1353c50023aa25af67bd563d28b6 [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#include "ConfigDescription.h"
Adam Lesinski769de982015-04-10 19:43:55 -070019#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "ValueVisitor.h"
22#include "util/Util.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080023
Adam Lesinskice5e56e2016-10-21 17:56:45 -070024#include <android-base/logging.h>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080025#include <androidfw/ResourceTypes.h>
Adam Lesinskicacb28f2016-10-19 12:18:14 -070026#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080027#include <memory>
28#include <string>
29#include <tuple>
30
31namespace aapt {
32
Adam Lesinskice5e56e2016-10-21 17:56:45 -070033static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
34 ResourceType rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070035 return lhs->type < rhs;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080036}
37
Adam Lesinski1ab598f2015-08-14 14:26:04 -070038template <typename T>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs,
40 const StringPiece& rhs) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070041 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080042}
43
Adam Lesinskice5e56e2016-10-21 17:56:45 -070044ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070045 const auto last = packages.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070046 auto iter =
47 std::lower_bound(packages.begin(), last, name,
48 less_than_struct_with_name<ResourceTablePackage>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049 if (iter != last && name == (*iter)->name) {
50 return iter->get();
51 }
52 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080053}
54
Adam Lesinskice5e56e2016-10-21 17:56:45 -070055ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 for (auto& package : packages) {
57 if (package->id && package->id.value() == id) {
58 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080059 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070060 }
61 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080062}
63
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name,
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 Maybe<uint8_t> id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 ResourceTablePackage* package = FindOrCreatePackage(name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070067 if (id && !package->id) {
68 package->id = id;
Adam Lesinski9ba47d82015-10-13 11:37:10 -070069 return package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070070 }
71
72 if (id && package->id && package->id.value() != id.value()) {
73 return nullptr;
74 }
75 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080076}
77
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078ResourceTablePackage* ResourceTable::FindOrCreatePackage(
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 const StringPiece& name) {
80 const auto last = packages.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 auto iter =
82 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 Lesinskice5e56e2016-10-21 17:56:45 -070088 std::unique_ptr<ResourceTablePackage> new_package =
Adam Lesinskicacb28f2016-10-19 12:18:14 -070089 util::make_unique<ResourceTablePackage>();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 new_package->name = name.ToString();
91 return packages.emplace(iter, std::move(new_package))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092}
93
Adam Lesinskice5e56e2016-10-21 17:56:45 -070094ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070095 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070096 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070097 if (iter != last && (*iter)->type == type) {
98 return iter->get();
99 }
100 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700101}
102
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700104 const auto last = types.end();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700105 auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 if (iter != last && (*iter)->type == type) {
107 return iter->get();
108 }
109 return types.emplace(iter, new ResourceTableType(type))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110}
111
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700112ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700113 const auto last = entries.end();
114 auto iter = std::lower_bound(entries.begin(), last, name,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700115 less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116 if (iter != last && name == (*iter)->name) {
117 return iter->get();
118 }
119 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700120}
121
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700123 auto last = entries.end();
124 auto iter = std::lower_bound(entries.begin(), last, name,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700125 less_than_struct_with_name<ResourceEntry>);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 if (iter != last && name == (*iter)->name) {
127 return iter->get();
128 }
129 return entries.emplace(iter, new ResourceEntry(name))->get();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700130}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800131
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700132ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
133 return FindValue(config, StringPiece());
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800134}
135
136struct ConfigKey {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700137 const ConfigDescription* config;
138 const StringPiece& product;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800139};
140
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs,
142 const ConfigKey& rhs) {
143 int cmp = lhs->config.compare(*rhs.config);
144 if (cmp == 0) {
145 cmp = StringPiece(lhs->product).compare(rhs.product);
146 }
147 return cmp < 0;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800148}
149
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700150ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800151 const StringPiece& product) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152 auto iter = std::lower_bound(values.begin(), values.end(),
153 ConfigKey{&config, product}, ltConfigKeyRef);
154 if (iter != values.end()) {
155 ResourceConfigValue* value = iter->get();
156 if (value->config == config && StringPiece(value->product) == product) {
157 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800158 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159 }
160 return nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800161}
162
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700163ResourceConfigValue* ResourceEntry::FindOrCreateValue(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700164 const ConfigDescription& config, const StringPiece& product) {
165 auto iter = std::lower_bound(values.begin(), values.end(),
166 ConfigKey{&config, product}, ltConfigKeyRef);
167 if (iter != values.end()) {
168 ResourceConfigValue* value = iter->get();
169 if (value->config == config && StringPiece(value->product) == product) {
170 return value;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800171 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700172 }
173 ResourceConfigValue* newValue =
174 values
175 .insert(iter, util::make_unique<ResourceConfigValue>(config, product))
176 ->get();
177 return newValue;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800178}
179
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700180std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(
181 const ConfigDescription& config) {
182 std::vector<ResourceConfigValue*> results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800183
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700184 auto iter = values.begin();
185 for (; iter != values.end(); ++iter) {
186 ResourceConfigValue* value = iter->get();
187 if (value->config == config) {
188 results.push_back(value);
189 ++iter;
190 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800191 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700192 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800193
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 for (; iter != values.end(); ++iter) {
195 ResourceConfigValue* value = iter->get();
196 if (value->config == config) {
197 results.push_back(value);
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800198 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700199 }
200 return results;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800201}
202
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700203std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700204 const std::function<bool(ResourceConfigValue*)>& f) {
205 std::vector<ResourceConfigValue*> results;
206 for (auto& configValue : values) {
207 if (f(configValue.get())) {
208 results.push_back(configValue.get());
Adam Lesinski458b8772016-04-25 14:20:21 -0700209 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 }
211 return results;
Adam Lesinski458b8772016-04-25 14:20:21 -0700212}
213
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800214/**
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700215 * The default handler for collisions.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700216 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700217 * Typically, a weak value will be overridden by a strong value. An existing
218 * weak
Adam Lesinski8197cc462016-08-19 12:16:49 -0700219 * value will not be overridden by an incoming weak value.
220 *
221 * There are some exceptions:
222 *
223 * Attributes: There are two types of Attribute values: USE and DECL.
224 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700225 * USE is anywhere an Attribute is declared without a format, and in a place
226 * that would
Adam Lesinski8197cc462016-08-19 12:16:49 -0700227 * be legal to declare if the Attribute already existed. This is typically in a
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700228 * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
229 * weak.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700230 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700231 * DECL is an absolute declaration of an Attribute and specifies an explicit
232 * format.
Adam Lesinski8197cc462016-08-19 12:16:49 -0700233 *
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 * A DECL will override a USE without error. Two DECLs must match in their
235 * format for there to be
Adam Lesinski8197cc462016-08-19 12:16:49 -0700236 * no error.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800237 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700238ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700239 Value* existing, Value* incoming) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700240 Attribute* existing_attr = ValueCast<Attribute>(existing);
241 Attribute* incoming_attr = ValueCast<Attribute>(incoming);
242 if (!incoming_attr) {
243 if (incoming->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700244 // We're trying to add a weak resource but a resource
245 // already exists. Keep the existing.
246 return CollisionResult::kKeepOriginal;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700247 } else if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 // Override the weak resource with the new strong resource.
249 return CollisionResult::kTakeNew;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800250 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700251 // The existing and incoming values are strong, this is an error
252 // if the values are not both attributes.
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700253 return CollisionResult::kConflict;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254 }
255
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700256 if (!existing_attr) {
257 if (existing->IsWeak()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700258 // The existing value is not an attribute and it is weak,
259 // so take the incoming attribute value.
260 return CollisionResult::kTakeNew;
261 }
262 // The existing value is not an attribute and it is strong,
263 // so the incoming attribute value is an error.
264 return CollisionResult::kConflict;
265 }
266
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700267 CHECK(incoming_attr != nullptr && existing_attr != nullptr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700268
269 //
270 // Attribute specific handling. At this point we know both
271 // values are attributes. Since we can declare and define
272 // attributes all-over, we do special handling to see
273 // which definition sticks.
274 //
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700275 if (existing_attr->type_mask == incoming_attr->type_mask) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276 // The two attributes are both DECLs, but they are plain attributes
277 // with the same formats.
278 // Keep the strongest one.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700279 return existing_attr->IsWeak() ? CollisionResult::kTakeNew
280 : CollisionResult::kKeepOriginal;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700281 }
282
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700283 if (existing_attr->IsWeak() &&
284 existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700285 // Any incoming attribute is better than this.
286 return CollisionResult::kTakeNew;
287 }
288
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700289 if (incoming_attr->IsWeak() &&
290 incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 // The incoming attribute may be a USE instead of a DECL.
292 // Keep the existing attribute.
293 return CollisionResult::kKeepOriginal;
294 }
295 return CollisionResult::kConflict;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800296}
297
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700298static constexpr const char* kValidNameChars = "._-";
299static constexpr const char* kValidNameMangledChars = "._-$";
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 Lesinskice5e56e2016-10-21 17:56:45 -0700306 return AddResourceImpl(name, {}, config, product, std::move(value),
307 kValidNameChars, 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 Lesinskice5e56e2016-10-21 17:56:45 -0700316 return AddResourceImpl(name, res_id, config, product, std::move(value),
317 kValidNameChars, 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 Lesinskice5e56e2016-10-21 17:56:45 -0700325 return AddFileReferenceImpl(name, config, source, path, nullptr,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700326 kValidNameChars, diag);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800327}
328
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700329bool ResourceTable::AddFileReferenceAllowMangled(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700330 const ResourceNameRef& name, const ConfigDescription& config,
331 const Source& source, const StringPiece& path, io::IFile* file,
332 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700333 return AddFileReferenceImpl(name, config, source, path, file,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700334 kValidNameMangledChars, diag);
Adam Lesinski355f2852016-02-13 20:26:45 -0800335}
336
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700337bool ResourceTable::AddFileReferenceImpl(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700338 const ResourceNameRef& name, const ConfigDescription& config,
339 const Source& source, const StringPiece& path, io::IFile* file,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700340 const char* valid_chars, IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700341 std::unique_ptr<FileReference> fileRef =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700342 util::make_unique<FileReference>(string_pool.MakeRef(path));
343 fileRef->SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700344 fileRef->file = file;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700345 return AddResourceImpl(name, ResourceId{}, config, StringPiece{},
346 std::move(fileRef), valid_chars, ResolveValueCollision,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700347 diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700348}
349
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700350bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
Adam Lesinski330edcd2015-05-04 17:40:56 -0700351 const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800352 const StringPiece& product,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353 std::unique_ptr<Value> value,
354 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
356 kValidNameMangledChars, ResolveValueCollision, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700357}
358
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700359bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700360 const ResourceId& id,
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700361 const ConfigDescription& config,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800362 const StringPiece& product,
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700363 std::unique_ptr<Value> value,
364 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700365 return AddResourceImpl(name, id, config, product, std::move(value),
366 kValidNameMangledChars, ResolveValueCollision, diag);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700367}
368
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700369bool ResourceTable::AddResourceImpl(
370 const ResourceNameRef& name, const ResourceId& res_id,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 const ConfigDescription& config, const StringPiece& product,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700372 std::unique_ptr<Value> value, const char* valid_chars,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700373 const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700374 CHECK(value != nullptr);
375 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700376
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700377 auto bad_char_iter =
378 util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
379 if (bad_char_iter != name.entry.end()) {
380 diag->Error(DiagMessage(value->GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700381 << "resource '" << name << "' has invalid entry name '"
382 << name.entry << "'. Invalid character '"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700383 << StringPiece(bad_char_iter, 1) << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700384 return false;
385 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800386
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700387 ResourceTablePackage* package = FindOrCreatePackage(name.package);
388 if (res_id.is_valid() && package->id &&
389 package->id.value() != res_id.package_id()) {
390 diag->Error(DiagMessage(value->GetSource())
391 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700392 << " but package '" << package->name << "' already has ID "
393 << std::hex << (int)package->id.value() << std::dec);
394 return false;
395 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800396
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700397 ResourceTableType* type = package->FindOrCreateType(name.type);
398 if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
399 diag->Error(DiagMessage(value->GetSource())
400 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700401 << " but type '" << type->type << "' already has ID "
402 << std::hex << (int)type->id.value() << std::dec);
403 return false;
404 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800405
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700406 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
407 if (res_id.is_valid() && entry->id &&
408 entry->id.value() != res_id.entry_id()) {
409 diag->Error(DiagMessage(value->GetSource())
410 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700411 << " but resource already has ID "
412 << ResourceId(package->id.value(), type->id.value(),
413 entry->id.value()));
414 return false;
415 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700416
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700417 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
418 if (!config_value->value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700419 // Resource does not exist, add it now.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700420 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700421
422 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700423 switch (conflictResolver(config_value->value.get(), value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700424 case CollisionResult::kTakeNew:
425 // Take the incoming value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700426 config_value->value = std::move(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700427 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800428
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700429 case CollisionResult::kConflict:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700430 diag->Error(DiagMessage(value->GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 << "duplicate value for resource '" << name << "' "
432 << "with config '" << config << "'");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700433 diag->Error(DiagMessage(config_value->value->GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700434 << "resource previously defined here");
435 return false;
Adam Lesinski5c3464c2016-08-24 16:03:48 -0700436
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700437 case CollisionResult::kKeepOriginal:
438 break;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800439 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700440 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800441
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700442 if (res_id.is_valid()) {
443 package->id = res_id.package_id();
444 type->id = res_id.type_id();
445 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700446 }
447 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800448}
449
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700450bool ResourceTable::SetSymbolState(const ResourceNameRef& name,
451 const ResourceId& res_id,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700452 const Symbol& symbol, IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700453 return SetSymbolStateImpl(name, res_id, symbol, kValidNameChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700454}
455
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700456bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
457 const ResourceId& res_id,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700458 const Symbol& symbol,
459 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700460 return SetSymbolStateImpl(name, res_id, symbol, kValidNameMangledChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700461}
462
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700463bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name,
464 const ResourceId& res_id,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700465 const Symbol& symbol,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700466 const char* valid_chars,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700467 IDiagnostics* diag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700468 CHECK(diag != nullptr);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700469
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700470 auto bad_char_iter =
471 util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
472 if (bad_char_iter != name.entry.end()) {
473 diag->Error(DiagMessage(symbol.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700474 << "resource '" << name << "' has invalid entry name '"
475 << name.entry << "'. Invalid character '"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700476 << StringPiece(bad_char_iter, 1) << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700477 return false;
478 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800479
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700480 ResourceTablePackage* package = FindOrCreatePackage(name.package);
481 if (res_id.is_valid() && package->id &&
482 package->id.value() != res_id.package_id()) {
483 diag->Error(DiagMessage(symbol.source)
484 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700485 << " but package '" << package->name << "' already has ID "
486 << std::hex << (int)package->id.value() << std::dec);
487 return false;
488 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800489
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700490 ResourceTableType* type = package->FindOrCreateType(name.type);
491 if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) {
492 diag->Error(DiagMessage(symbol.source)
493 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700494 << " but type '" << type->type << "' already has ID "
495 << std::hex << (int)type->id.value() << std::dec);
496 return false;
497 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700498
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700499 ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
500 if (res_id.is_valid() && entry->id &&
501 entry->id.value() != res_id.entry_id()) {
502 diag->Error(DiagMessage(symbol.source)
503 << "trying to add resource '" << name << "' with ID " << res_id
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700504 << " but resource already has ID "
505 << ResourceId(package->id.value(), type->id.value(),
506 entry->id.value()));
507 return false;
508 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800509
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700510 if (res_id.is_valid()) {
511 package->id = res_id.package_id();
512 type->id = res_id.type_id();
513 entry->id = res_id.entry_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700514 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800515
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700516 // Only mark the type state as public, it doesn't care about being private.
517 if (symbol.state == SymbolState::kPublic) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700518 type->symbol_status.state = SymbolState::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700519 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800520
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700521 if (symbol.state == SymbolState::kUndefined &&
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700522 entry->symbol_status.state != SymbolState::kUndefined) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700523 // We can't undefine a symbol (remove its visibility). Ignore.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800524 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700525 }
526
527 if (symbol.state == SymbolState::kPrivate &&
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700528 entry->symbol_status.state == SymbolState::kPublic) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700529 // We can't downgrade public to private. Ignore.
530 return true;
531 }
532
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700533 entry->symbol_status = std::move(symbol);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700534 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800535}
536
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700537Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700538 const ResourceNameRef& name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700539 ResourceTablePackage* package = FindPackage(name.package);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700540 if (!package) {
541 return {};
542 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800543
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700544 ResourceTableType* type = package->FindType(name.type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700545 if (!type) {
546 return {};
547 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800548
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700549 ResourceEntry* entry = type->FindEntry(name.entry);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700550 if (!entry) {
551 return {};
552 }
553 return SearchResult{package, type, entry};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800554}
555
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700556} // namespace aapt