blob: a1e7d36d91d9ccc1e90e962bc7b2ecf8540e1d0d [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
17#include "ConfigDescription.h"
Adam Lesinski769de982015-04-10 19:43:55 -070018#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019#include "ResourceTable.h"
20#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
24#include <algorithm>
25#include <androidfw/ResourceTypes.h>
26#include <memory>
27#include <string>
28#include <tuple>
29
30namespace aapt {
31
32static bool compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) {
33 return lhs.config < rhs;
34}
35
36static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
37 return lhs->type < rhs;
38}
39
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040template <typename T>
41static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
42 const StringPiece16& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
44}
45
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) {
47 const auto last = packages.end();
48 auto iter = std::lower_bound(packages.begin(), last, name,
49 lessThanStructWithName<ResourceTablePackage>);
50 if (iter != last && name == (*iter)->name) {
51 return iter->get();
52 }
53 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054}
55
Adam Lesinski1ab598f2015-08-14 14:26:04 -070056ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
57 for (auto& package : packages) {
58 if (package->id && package->id.value() == id) {
59 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080060 }
61 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080063}
64
Adam Lesinski1ab598f2015-08-14 14:26:04 -070065ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, uint8_t id) {
66 ResourceTablePackage* package = findOrCreatePackage(name);
67 if (!package->id) {
68 package->id = id;
69 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080070 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070071
72 if (package->id.value() == id) {
73 return package;
74 }
75 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080076}
77
Adam Lesinski1ab598f2015-08-14 14:26:04 -070078ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) {
79 const auto last = packages.end();
80 auto iter = std::lower_bound(packages.begin(), last, name,
81 lessThanStructWithName<ResourceTablePackage>);
82 if (iter != last && name == (*iter)->name) {
83 return iter->get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080084 }
85
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086 std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
87 newPackage->name = name.toString();
88 return packages.emplace(iter, std::move(newPackage))->get();
89}
90
91ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
92 const auto last = types.end();
93 auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
94 if (iter != last && (*iter)->type == type) {
95 return iter->get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070097 return nullptr;
98}
99
100ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
101 const auto last = types.end();
102 auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
103 if (iter != last && (*iter)->type == type) {
104 return iter->get();
105 }
106 return types.emplace(iter, new ResourceTableType{ type })->get();
107}
108
109ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
110 const auto last = entries.end();
111 auto iter = std::lower_bound(entries.begin(), last, name,
112 lessThanStructWithName<ResourceEntry>);
113 if (iter != last && name == (*iter)->name) {
114 return iter->get();
115 }
116 return nullptr;
117}
118
119ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) {
120 auto last = entries.end();
121 auto iter = std::lower_bound(entries.begin(), last, name,
122 lessThanStructWithName<ResourceEntry>);
123 if (iter != last && name == (*iter)->name) {
124 return iter->get();
125 }
126 return entries.emplace(iter, new ResourceEntry{ name })->get();
127}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800128
129/**
130 * The default handler for collisions. A return value of -1 means keep the
131 * existing value, 0 means fail, and +1 means take the incoming value.
132 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
134 Attribute* existingAttr = valueCast<Attribute>(existing);
135 Attribute* incomingAttr = valueCast<Attribute>(incoming);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800136
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 if (!incomingAttr) {
138 if (incoming->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800139 // We're trying to add a weak resource but a resource
140 // already exists. Keep the existing.
141 return -1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142 } else if (existing->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800143 // Override the weak resource with the new strong resource.
144 return 1;
145 }
146 // The existing and incoming values are strong, this is an error
147 // if the values are not both attributes.
148 return 0;
149 }
150
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 if (!existingAttr) {
152 if (existing->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800153 // The existing value is not an attribute and it is weak,
154 // so take the incoming attribute value.
155 return 1;
156 }
157 // The existing value is not an attribute and it is strong,
158 // so the incoming attribute value is an error.
159 return 0;
160 }
161
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700162 assert(incomingAttr && existingAttr);
163
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800164 //
165 // Attribute specific handling. At this point we know both
166 // values are attributes. Since we can declare and define
167 // attributes all-over, we do special handling to see
168 // which definition sticks.
169 //
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700170 if (existingAttr->typeMask == incomingAttr->typeMask) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800171 // The two attributes are both DECLs, but they are plain attributes
172 // with the same formats.
173 // Keep the strongest one.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700174 return existingAttr->isWeak() ? 1 : -1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800175 }
176
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700177 if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800178 // Any incoming attribute is better than this.
179 return 1;
180 }
181
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700182 if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800183 // The incoming attribute may be a USE instead of a DECL.
184 // Keep the existing attribute.
185 return -1;
186 }
187 return 0;
188}
189
190static constexpr const char16_t* kValidNameChars = u"._-";
Adam Lesinski330edcd2015-05-04 17:40:56 -0700191static constexpr const char16_t* kValidNameMangledChars = u"._-$";
192
193bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700194 const Source& source, std::unique_ptr<Value> value,
195 IDiagnostics* diag) {
196 return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars,
197 diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700198}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800199
200bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700201 const ConfigDescription& config, const Source& source,
202 std::unique_ptr<Value> value, IDiagnostics* diag) {
203 return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars, diag);
204}
205
206bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
207 const Source& source, const StringPiece16& path,
208 IDiagnostics* diag) {
209 return addResourceImpl(name, ResourceId{}, config, source,
210 util::make_unique<FileReference>(stringPool.makeRef(path)),
211 kValidNameChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700212}
213
214bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
215 const ConfigDescription& config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216 const Source& source,
217 std::unique_ptr<Value> value,
218 IDiagnostics* diag) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700219 return addResourceImpl(name, ResourceId{}, config, source, std::move(value),
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 kValidNameMangledChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700221}
222
223bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700224 const ConfigDescription& config, const Source& source,
225 std::unique_ptr<Value> value, const char16_t* validChars,
226 IDiagnostics* diag) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700227 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800228 if (badCharIter != name.entry.end()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700229 diag->error(DiagMessage(source)
230 << "resource '"
231 << name
232 << "' has invalid entry name '"
233 << name.entry
234 << "'. Invalid character '"
235 << StringPiece16(badCharIter, 1)
236 << "'");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800237 return false;
238 }
239
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700240 ResourceTablePackage* package = findOrCreatePackage(name.package);
241 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
242 diag->error(DiagMessage(source)
243 << "trying to add resource '"
244 << name
245 << "' with ID "
246 << resId
247 << " but package '"
248 << package->name
249 << "' already has ID "
250 << std::hex << (int) package->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800251 return false;
252 }
253
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700254 ResourceTableType* type = package->findOrCreateType(name.type);
255 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
256 diag->error(DiagMessage(source)
257 << "trying to add resource '"
258 << name
259 << "' with ID "
260 << resId
261 << " but type '"
262 << type->type
263 << "' already has ID "
264 << std::hex << (int) type->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800265 return false;
266 }
267
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700268 ResourceEntry* entry = type->findOrCreateEntry(name.entry);
269 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
270 diag->error(DiagMessage(source)
271 << "trying to add resource '"
272 << name
273 << "' with ID "
274 << resId
275 << " but resource already has ID "
276 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
277 return false;
278 }
279
280 const auto endIter = entry->values.end();
281 auto iter = std::lower_bound(entry->values.begin(), endIter, config, compareConfigs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800282 if (iter == endIter || iter->config != config) {
283 // This resource did not exist before, add it.
284 entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) });
285 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700286 int collisionResult = resolveValueCollision(iter->value.get(), value.get());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800287 if (collisionResult > 0) {
288 // Take the incoming value.
289 *iter = ResourceConfigValue{ config, source, {}, std::move(value) };
290 } else if (collisionResult == 0) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700291 diag->error(DiagMessage(source)
292 << "duplicate value for resource '" << name << "' "
293 << "with config '" << iter->config << "'");
294 diag->error(DiagMessage(iter->source)
295 << "resource previously defined here");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800296 return false;
297 }
298 }
299
300 if (resId.isValid()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700301 package->id = resId.packageId();
302 type->id = resId.typeId();
303 entry->id = resId.entryId();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800304 }
305 return true;
306}
307
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800308bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700309 const Source& source, IDiagnostics* diag) {
310 return markPublicImpl(name, resId, source, kValidNameChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700311}
312
313bool ResourceTable::markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700314 const Source& source, IDiagnostics* diag) {
315 return markPublicImpl(name, resId, source, kValidNameMangledChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700316}
317
318bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700319 const Source& source, const char16_t* validChars,
320 IDiagnostics* diag) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700321 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800322 if (badCharIter != name.entry.end()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700323 diag->error(DiagMessage(source)
324 << "resource '"
325 << name
326 << "' has invalid entry name '"
327 << name.entry
328 << "'. Invalid character '"
329 << StringPiece16(badCharIter, 1)
330 << "'");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800331 return false;
332 }
333
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700334 ResourceTablePackage* package = findOrCreatePackage(name.package);
335 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
336 diag->error(DiagMessage(source)
337 << "trying to add resource '"
338 << name
339 << "' with ID "
340 << resId
341 << " but package '"
342 << package->name
343 << "' already has ID "
344 << std::hex << (int) package->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800345 return false;
346 }
347
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700348 ResourceTableType* type = package->findOrCreateType(name.type);
349 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
350 diag->error(DiagMessage(source)
351 << "trying to add resource '"
352 << name
353 << "' with ID "
354 << resId
355 << " but type '"
356 << type->type
357 << "' already has ID "
358 << std::hex << (int) type->id.value() << std::dec);
359 return false;
360 }
361
362 ResourceEntry* entry = type->findOrCreateEntry(name.entry);
363 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
364 diag->error(DiagMessage(source)
365 << "trying to add resource '"
366 << name
367 << "' with ID "
368 << resId
369 << " but resource already has ID "
370 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800371 return false;
372 }
373
374 type->publicStatus.isPublic = true;
375 entry->publicStatus.isPublic = true;
Adam Lesinski6ff19662015-04-30 17:40:46 -0700376 entry->publicStatus.source = source;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800377
378 if (resId.isValid()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700379 package->id = resId.packageId();
380 type->id = resId.typeId();
381 entry->id = resId.entryId();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800382 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800383 return true;
384}
385
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700386Maybe<ResourceTable::SearchResult>
387ResourceTable::findResource(const ResourceNameRef& name) {
388 ResourceTablePackage* package = findPackage(name.package);
389 if (!package) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700390 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800391 }
392
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700393 ResourceTableType* type = package->findType(name.type);
394 if (!type) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700395 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800396 }
397
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700398 ResourceEntry* entry = type->findEntry(name.entry);
399 if (!entry) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700400 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800401 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700402 return SearchResult{ package, type, entry };
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800403}
404
405} // namespace aapt