blob: 02be6516f5709ec0d212b3c4bba640a577a9cf76 [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"
18#include "Logger.h"
Adam Lesinski769de982015-04-10 19:43:55 -070019#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include "ResourceTable.h"
21#include "ResourceValues.h"
22#include "Util.h"
23
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
40static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const StringPiece16& rhs) {
41 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
42}
43
44ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) {
45}
46
47std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) {
48 auto last = mTypes.end();
49 auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType);
50 if (iter != last) {
51 if ((*iter)->type == type) {
52 return *iter;
53 }
54 }
55 return *mTypes.emplace(iter, new ResourceTableType{ type });
56}
57
58std::unique_ptr<ResourceEntry>& ResourceTable::findOrCreateEntry(
59 std::unique_ptr<ResourceTableType>& type, const StringPiece16& name) {
60 auto last = type->entries.end();
61 auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry);
62 if (iter != last) {
63 if (name == (*iter)->name) {
64 return *iter;
65 }
66 }
67 return *type->entries.emplace(iter, new ResourceEntry{ name });
68}
69
70struct IsAttributeVisitor : ConstValueVisitor {
71 bool isAttribute = false;
72
73 void visit(const Attribute&, ValueVisitorArgs&) override {
74 isAttribute = true;
75 }
76
77 operator bool() {
78 return isAttribute;
79 }
80};
81
82/**
83 * The default handler for collisions. A return value of -1 means keep the
84 * existing value, 0 means fail, and +1 means take the incoming value.
85 */
86static int defaultCollisionHandler(const Value& existing, const Value& incoming) {
87 IsAttributeVisitor existingIsAttr, incomingIsAttr;
88 existing.accept(existingIsAttr, {});
89 incoming.accept(incomingIsAttr, {});
90
91 if (!incomingIsAttr) {
92 if (incoming.isWeak()) {
93 // We're trying to add a weak resource but a resource
94 // already exists. Keep the existing.
95 return -1;
96 } else if (existing.isWeak()) {
97 // Override the weak resource with the new strong resource.
98 return 1;
99 }
100 // The existing and incoming values are strong, this is an error
101 // if the values are not both attributes.
102 return 0;
103 }
104
105 if (!existingIsAttr) {
106 if (existing.isWeak()) {
107 // The existing value is not an attribute and it is weak,
108 // so take the incoming attribute value.
109 return 1;
110 }
111 // The existing value is not an attribute and it is strong,
112 // so the incoming attribute value is an error.
113 return 0;
114 }
115
116 //
117 // Attribute specific handling. At this point we know both
118 // values are attributes. Since we can declare and define
119 // attributes all-over, we do special handling to see
120 // which definition sticks.
121 //
122 const Attribute& existingAttr = static_cast<const Attribute&>(existing);
123 const Attribute& incomingAttr = static_cast<const Attribute&>(incoming);
124 if (existingAttr.typeMask == incomingAttr.typeMask) {
125 // The two attributes are both DECLs, but they are plain attributes
126 // with the same formats.
127 // Keep the strongest one.
128 return existingAttr.isWeak() ? 1 : -1;
129 }
130
131 if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
132 // Any incoming attribute is better than this.
133 return 1;
134 }
135
136 if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
137 // The incoming attribute may be a USE instead of a DECL.
138 // Keep the existing attribute.
139 return -1;
140 }
141 return 0;
142}
143
144static constexpr const char16_t* kValidNameChars = u"._-";
145
146bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
147 const ConfigDescription& config, const SourceLine& source,
148 std::unique_ptr<Value> value) {
149 if (!name.package.empty() && name.package != mPackage) {
150 Logger::error(source)
151 << "resource '"
152 << name
153 << "' has incompatible package. Must be '"
154 << mPackage
155 << "'."
156 << std::endl;
157 return false;
158 }
159
160 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
161 if (badCharIter != name.entry.end()) {
162 Logger::error(source)
163 << "resource '"
164 << name
165 << "' has invalid entry name '"
166 << name.entry
167 << "'. Invalid character '"
Adam Lesinskica2fc352015-04-03 12:08:26 -0700168 << StringPiece16(badCharIter, 1)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800169 << "'."
170 << std::endl;
171 return false;
172 }
173
174 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
175 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
176 type->typeId != resId.typeId()) {
177 Logger::error(source)
178 << "trying to add resource '"
179 << name
180 << "' with ID "
181 << resId
182 << " but type '"
183 << type->type
184 << "' already has ID "
185 << std::hex << type->typeId << std::dec
186 << "."
187 << std::endl;
188 return false;
189 }
190
191 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
192 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
193 entry->entryId != resId.entryId()) {
194 Logger::error(source)
195 << "trying to add resource '"
196 << name
197 << "' with ID "
198 << resId
199 << " but resource already has ID "
200 << ResourceId(mPackageId, type->typeId, entry->entryId)
201 << "."
202 << std::endl;
203 return false;
204 }
205
206 const auto endIter = std::end(entry->values);
207 auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs);
208 if (iter == endIter || iter->config != config) {
209 // This resource did not exist before, add it.
210 entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) });
211 } else {
212 int collisionResult = defaultCollisionHandler(*iter->value, *value);
213 if (collisionResult > 0) {
214 // Take the incoming value.
215 *iter = ResourceConfigValue{ config, source, {}, std::move(value) };
216 } else if (collisionResult == 0) {
217 Logger::error(source)
218 << "duplicate value for resource '" << name << "' "
219 << "with config '" << iter->config << "'."
220 << std::endl;
221
222 Logger::error(iter->source)
223 << "resource previously defined here."
224 << std::endl;
225 return false;
226 }
227 }
228
229 if (resId.isValid()) {
230 type->typeId = resId.typeId();
231 entry->entryId = resId.entryId();
232 }
233 return true;
234}
235
236bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
237 const SourceLine& source, std::unique_ptr<Value> value) {
238 return addResource(name, ResourceId{}, config, source, std::move(value));
239}
240
241bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId,
242 const SourceLine& source) {
243 if (!name.package.empty() && name.package != mPackage) {
244 Logger::error(source)
245 << "resource '"
246 << name
247 << "' has incompatible package. Must be '"
248 << mPackage
249 << "'."
250 << std::endl;
251 return false;
252 }
253
254 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
255 if (badCharIter != name.entry.end()) {
256 Logger::error(source)
257 << "resource '"
258 << name
259 << "' has invalid entry name '"
260 << name.entry
261 << "'. Invalid character '"
Adam Lesinskica2fc352015-04-03 12:08:26 -0700262 << StringPiece16(badCharIter, 1)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800263 << "'."
264 << std::endl;
265 return false;
266 }
267
268 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
269 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
270 type->typeId != resId.typeId()) {
271 Logger::error(source)
272 << "trying to make resource '"
273 << name
274 << "' public with ID "
275 << resId
276 << " but type '"
277 << type->type
278 << "' already has ID "
279 << std::hex << type->typeId << std::dec
280 << "."
281 << std::endl;
282 return false;
283 }
284
285 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
286 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
287 entry->entryId != resId.entryId()) {
288 Logger::error(source)
289 << "trying to make resource '"
290 << name
291 << "' public with ID "
292 << resId
293 << " but resource already has ID "
294 << ResourceId(mPackageId, type->typeId, entry->entryId)
295 << "."
296 << std::endl;
297 return false;
298 }
299
300 type->publicStatus.isPublic = true;
301 entry->publicStatus.isPublic = true;
302
303 if (resId.isValid()) {
304 type->typeId = resId.typeId();
305 entry->entryId = resId.entryId();
306 }
307
308 if (entry->values.empty()) {
309 entry->values.push_back(ResourceConfigValue{ {}, source, {},
310 util::make_unique<Sentinel>() });
311 }
312 return true;
313}
314
Adam Lesinski769de982015-04-10 19:43:55 -0700315bool ResourceTable::merge(ResourceTable&& other) {
316 const bool mangleNames = mPackage != other.getPackage();
317 std::u16string mangledName;
318
319 for (auto& otherType : other) {
320 std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type);
321 if (type->publicStatus.isPublic && otherType->publicStatus.isPublic &&
322 type->typeId != otherType->typeId) {
323 Logger::error() << "can not merge type '" << type->type << "': conflicting public IDs "
324 << "(" << type->typeId << " vs " << otherType->typeId << ")."
325 << std::endl;
326 return false;
327 }
328
329 for (auto& otherEntry : otherType->entries) {
330 const std::u16string* nameToAdd = &otherEntry->name;
331 if (mangleNames) {
332 mangledName = otherEntry->name;
333 NameMangler::mangle(other.getPackage(), &mangledName);
334 nameToAdd = &mangledName;
335 }
336
337 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd);
338 if (entry->publicStatus.isPublic && otherEntry->publicStatus.isPublic &&
339 entry->entryId != otherEntry->entryId) {
340 Logger::error() << "can not merge entry '" << type->type << "/" << entry->name
341 << "': conflicting public IDs "
342 << "(" << entry->entryId << " vs " << entry->entryId << ")."
343 << std::endl;
344 return false;
345 }
346
347 for (ResourceConfigValue& otherValue : otherEntry->values) {
348 auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
349 otherValue.config, compareConfigs);
350 if (iter != entry->values.end() && iter->config == otherValue.config) {
351 int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value);
352 if (collisionResult > 0) {
353 // Take the incoming value.
354 iter->source = std::move(otherValue.source);
355 iter->comment = std::move(otherValue.comment);
356 iter->value = std::unique_ptr<Value>(otherValue.value->clone(&mValuePool));
357 } else if (collisionResult == 0) {
358 ResourceNameRef resourceName = { mPackage, type->type, entry->name };
359 Logger::error(otherValue.source)
360 << "resource '" << resourceName << "' has a conflicting value for "
361 << "configuration (" << otherValue.config << ")."
362 << std::endl;
363 Logger::note(iter->source) << "originally defined here." << std::endl;
364 return false;
365 }
366 } else {
367 entry->values.insert(iter, ResourceConfigValue{
368 otherValue.config,
369 std::move(otherValue.source),
370 std::move(otherValue.comment),
371 std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)),
372 });
373 }
374 }
375 }
376 }
377 return true;
378}
379
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800380std::tuple<const ResourceTableType*, const ResourceEntry*>
381ResourceTable::findResource(const ResourceNameRef& name) const {
382 if (name.package != mPackage) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700383 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800384 }
385
386 auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType);
387 if (iter == mTypes.end() || (*iter)->type != name.type) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700388 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800389 }
390
391 const std::unique_ptr<ResourceTableType>& type = *iter;
392 auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry,
393 lessThanEntry);
394 if (iter2 == type->entries.end() || name.entry != (*iter2)->name) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700395 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800396 }
Adam Lesinskica2fc352015-04-03 12:08:26 -0700397 return std::make_tuple(iter->get(), iter2->get());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800398}
399
400} // namespace aapt