blob: 326a2ac091a751797c44c4a4ea6b3e3016c9148c [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 "BinaryResourceParser.h"
18#include "Logger.h"
19#include "ResChunkPullParser.h"
Adam Lesinski769de982015-04-10 19:43:55 -070020#include "Resolver.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080021#include "ResourceParser.h"
22#include "ResourceTable.h"
23#include "ResourceTypeExtensions.h"
24#include "ResourceValues.h"
25#include "Source.h"
26#include "Util.h"
27
28#include <androidfw/ResourceTypes.h>
29#include <androidfw/TypeWrappers.h>
30#include <map>
31#include <string>
32
33namespace aapt {
34
35using namespace android;
36
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080037/*
38 * Visitor that converts a reference's resource ID to a resource name,
39 * given a mapping from resource ID to resource name.
40 */
41struct ReferenceIdToNameVisitor : ValueVisitor {
Adam Lesinski769de982015-04-10 19:43:55 -070042 ReferenceIdToNameVisitor(const std::shared_ptr<Resolver>& resolver,
43 std::map<ResourceId, ResourceName>* cache) :
44 mResolver(resolver), mCache(cache) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080045 }
46
47 void visit(Reference& reference, ValueVisitorArgs&) override {
48 idToName(reference);
49 }
50
51 void visit(Attribute& attr, ValueVisitorArgs&) override {
52 for (auto& entry : attr.symbols) {
53 idToName(entry.symbol);
54 }
55 }
56
57 void visit(Style& style, ValueVisitorArgs&) override {
58 if (style.parent.id.isValid()) {
59 idToName(style.parent);
60 }
61
62 for (auto& entry : style.entries) {
63 idToName(entry.key);
64 entry.value->accept(*this, {});
65 }
66 }
67
68 void visit(Styleable& styleable, ValueVisitorArgs&) override {
69 for (auto& attr : styleable.entries) {
70 idToName(attr);
71 }
72 }
73
74 void visit(Array& array, ValueVisitorArgs&) override {
75 for (auto& item : array.items) {
76 item->accept(*this, {});
77 }
78 }
79
80 void visit(Plural& plural, ValueVisitorArgs&) override {
81 for (auto& item : plural.values) {
82 if (item) {
83 item->accept(*this, {});
84 }
85 }
86 }
87
88private:
89 void idToName(Reference& reference) {
90 if (!reference.id.isValid()) {
91 return;
92 }
93
Adam Lesinski769de982015-04-10 19:43:55 -070094 auto cacheIter = mCache->find(reference.id);
95 if (cacheIter != mCache->end()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 reference.name = cacheIter->second;
97 reference.id = 0;
Adam Lesinski769de982015-04-10 19:43:55 -070098 } else {
99 const android::ResTable& table = mResolver->getResTable();
100 android::ResTable::resource_name resourceName;
101 if (table.getResourceName(reference.id.id, false, &resourceName)) {
102 const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
103 resourceName.typeLen));
104 assert(type);
105 reference.name.package.assign(resourceName.package, resourceName.packageLen);
106 reference.name.type = *type;
107 reference.name.entry.assign(resourceName.name, resourceName.nameLen);
108 reference.id = 0;
109
110 // Add to cache.
111 mCache->insert({reference.id, reference.name});
112 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800113 }
114 }
115
Adam Lesinski769de982015-04-10 19:43:55 -0700116 std::shared_ptr<Resolver> mResolver;
117 std::map<ResourceId, ResourceName>* mCache;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800118};
119
120
Adam Lesinski769de982015-04-10 19:43:55 -0700121BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
122 const std::shared_ptr<Resolver>& resolver,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800123 const Source& source,
124 const void* data,
125 size_t len) :
Adam Lesinski769de982015-04-10 19:43:55 -0700126 mTable(table), mResolver(resolver), mSource(source), mData(data), mDataLen(len) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800127}
128
129bool BinaryResourceParser::parse() {
130 ResChunkPullParser parser(mData, mDataLen);
131
132 bool error = false;
133 while(ResChunkPullParser::isGoodEvent(parser.next())) {
134 if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
135 Logger::warn(mSource)
136 << "unknown chunk of type '"
137 << parser.getChunk()->type
138 << "'."
139 << std::endl;
140 continue;
141 }
142
143 error |= !parseTable(parser.getChunk());
144 }
145
146 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
147 Logger::error(mSource)
148 << "bad document: "
149 << parser.getLastError()
150 << "."
151 << std::endl;
152 return false;
153 }
154 return !error;
155}
156
157bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
158 if (!mSymbolEntries || mSymbolEntryCount == 0) {
159 return false;
160 }
161
Adam Lesinskica2fc352015-04-03 12:08:26 -0700162 if (reinterpret_cast<uintptr_t>(data) < reinterpret_cast<uintptr_t>(mData)) {
163 return false;
164 }
165
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800166 // We only support 32 bit offsets right now.
Adam Lesinskica2fc352015-04-03 12:08:26 -0700167 const uintptr_t offset = reinterpret_cast<uintptr_t>(data) -
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800168 reinterpret_cast<uintptr_t>(mData);
169 if (offset > std::numeric_limits<uint32_t>::max()) {
170 return false;
171 }
172
173 for (size_t i = 0; i < mSymbolEntryCount; i++) {
174 if (mSymbolEntries[i].offset == offset) {
175 // This offset is a symbol!
176 const StringPiece16 str = util::getString(mSymbolPool,
177 mSymbolEntries[i].stringIndex);
178 StringPiece16 typeStr;
179 ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr,
180 &outSymbol->entry);
181 const ResourceType* type = parseResourceType(typeStr);
182 if (!type) {
183 return false;
184 }
185 outSymbol->type = *type;
186
187 // Since we scan the symbol table in order, we can start looking for the
188 // next symbol from this point.
189 mSymbolEntryCount -= i + 1;
190 mSymbolEntries += i + 1;
191 return true;
192 }
193 }
194 return false;
195}
196
197bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
198 const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk);
199 if (!symbolTableHeader) {
200 Logger::error(mSource)
201 << "could not parse chunk as SymbolTable_header."
202 << std::endl;
203 return false;
204 }
205
206 const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry);
207 if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) {
208 Logger::error(mSource)
209 << "entries extend beyond chunk."
210 << std::endl;
211 return false;
212 }
213
214 mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>(
215 getChunkData(symbolTableHeader->header));
216 mSymbolEntryCount = symbolTableHeader->count;
217
218 ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes,
219 getChunkDataLen(symbolTableHeader->header) - entrySizeBytes);
220 if (!ResChunkPullParser::isGoodEvent(parser.next())) {
221 Logger::error(mSource)
222 << "failed to parse chunk: "
223 << parser.getLastError()
224 << "."
225 << std::endl;
226 return false;
227 }
228
229 if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) {
230 Logger::error(mSource)
231 << "expected Symbol string pool."
232 << std::endl;
233 return false;
234 }
235
Adam Lesinskica2fc352015-04-03 12:08:26 -0700236 if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800237 Logger::error(mSource)
238 << "failed to parse symbol string pool with code: "
239 << mSymbolPool.getError()
240 << "."
241 << std::endl;
242 return false;
243 }
244 return true;
245}
246
247bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
248 const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
249 if (!tableHeader) {
250 Logger::error(mSource)
251 << "could not parse chunk as ResTable_header."
252 << std::endl;
253 return false;
254 }
255
256 ResChunkPullParser parser(getChunkData(tableHeader->header),
257 getChunkDataLen(tableHeader->header));
258 while (ResChunkPullParser::isGoodEvent(parser.next())) {
259 switch (parser.getChunk()->type) {
260 case android::RES_STRING_POOL_TYPE:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700261 if (mValuePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800262 if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700263 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800264 Logger::error(mSource)
265 << "failed to parse value string pool with code: "
266 << mValuePool.getError()
267 << "."
268 << std::endl;
269 return false;
270 }
271
272 // Reserve some space for the strings we are going to add.
273 mTable->getValueStringPool().hintWillAdd(
274 mValuePool.size(), mValuePool.styleCount());
275 } else {
276 Logger::warn(mSource)
277 << "unexpected string pool."
278 << std::endl;
279 }
280 break;
281
282 case RES_TABLE_SYMBOL_TABLE_TYPE:
283 if (!parseSymbolTable(parser.getChunk())) {
284 return false;
285 }
286 break;
287
288 case RES_TABLE_SOURCE_POOL_TYPE: {
289 if (mSourcePool.setTo(getChunkData(*parser.getChunk()),
Adam Lesinskica2fc352015-04-03 12:08:26 -0700290 getChunkDataLen(*parser.getChunk())) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800291 Logger::error(mSource)
292 << "failed to parse source pool with code: "
293 << mSourcePool.getError()
294 << "."
295 << std::endl;
296 return false;
297 }
298 break;
299 }
300
301 case android::RES_TABLE_PACKAGE_TYPE:
302 if (!parsePackage(parser.getChunk())) {
303 return false;
304 }
305 break;
306
307 default:
308 Logger::warn(mSource)
309 << "unexpected chunk of type "
310 << parser.getChunk()->type
311 << "."
312 << std::endl;
313 break;
314 }
315 }
316
317 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
318 Logger::error(mSource)
319 << "bad resource table: " << parser.getLastError()
320 << "."
321 << std::endl;
322 return false;
323 }
324 return true;
325}
326
327bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700328 if (mValuePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800329 Logger::error(mSource)
330 << "no value string pool for ResTable."
331 << std::endl;
332 return false;
333 }
334
335 const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
336 if (!packageHeader) {
337 Logger::error(mSource)
338 << "could not parse chunk as ResTable_header."
339 << std::endl;
340 return false;
341 }
342
343 if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) {
344 // This is the first time the table has it's package ID set.
345 mTable->setPackageId(packageHeader->id);
346 } else if (mTable->getPackageId() != packageHeader->id) {
347 Logger::error(mSource)
348 << "ResTable_package has package ID "
349 << std::hex << packageHeader->id << std::dec
350 << " but ResourceTable has package ID "
351 << std::hex << mTable->getPackageId() << std::dec
352 << std::endl;
353 return false;
354 }
355
356 size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name),
357 sizeof(packageHeader->name) / sizeof(packageHeader->name[0]));
358 mTable->setPackage(StringPiece16(reinterpret_cast<const char16_t*>(packageHeader->name), len));
359
360 ResChunkPullParser parser(getChunkData(packageHeader->header),
361 getChunkDataLen(packageHeader->header));
362 while (ResChunkPullParser::isGoodEvent(parser.next())) {
363 switch (parser.getChunk()->type) {
364 case android::RES_STRING_POOL_TYPE:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700365 if (mTypePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800366 if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700367 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800368 Logger::error(mSource)
369 << "failed to parse type string pool with code "
370 << mTypePool.getError()
371 << "."
372 << std::endl;
373 return false;
374 }
Adam Lesinskica2fc352015-04-03 12:08:26 -0700375 } else if (mKeyPool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800376 if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700377 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800378 Logger::error(mSource)
379 << "failed to parse key string pool with code "
380 << mKeyPool.getError()
381 << "."
382 << std::endl;
383 return false;
384 }
385 } else {
386 Logger::warn(mSource)
387 << "unexpected string pool."
388 << std::endl;
389 }
390 break;
391
392 case android::RES_TABLE_TYPE_SPEC_TYPE:
393 if (!parseTypeSpec(parser.getChunk())) {
394 return false;
395 }
396 break;
397
398 case android::RES_TABLE_TYPE_TYPE:
399 if (!parseType(parser.getChunk())) {
400 return false;
401 }
402 break;
403
404 default:
405 Logger::warn(mSource)
406 << "unexpected chunk of type "
407 << parser.getChunk()->type
408 << "."
409 << std::endl;
410 break;
411 }
412 }
413
414 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
415 Logger::error(mSource)
416 << "bad package: "
417 << parser.getLastError()
418 << "."
419 << std::endl;
420 return false;
421 }
422
423 // Now go through the table and change resource ID references to
424 // symbolic references.
425
Adam Lesinski769de982015-04-10 19:43:55 -0700426 ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800427 for (auto& type : *mTable) {
428 for (auto& entry : type->entries) {
429 for (auto& configValue : entry->values) {
430 configValue.value->accept(visitor, {});
431 }
432 }
433 }
434 return true;
435}
436
437bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700438 if (mTypePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800439 Logger::error(mSource)
440 << "no type string pool available for ResTable_typeSpec."
441 << std::endl;
442 return false;
443 }
444
445 const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
446 if (!typeSpec) {
447 Logger::error(mSource)
448 << "could not parse chunk as ResTable_typeSpec."
449 << std::endl;
450 return false;
451 }
452
453 if (typeSpec->id == 0) {
454 Logger::error(mSource)
455 << "ResTable_typeSpec has invalid id: "
456 << typeSpec->id
457 << "."
458 << std::endl;
459 return false;
460 }
461 return true;
462}
463
464bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700465 if (mTypePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800466 Logger::error(mSource)
467 << "no type string pool available for ResTable_typeSpec."
468 << std::endl;
469 return false;
470 }
471
Adam Lesinskica2fc352015-04-03 12:08:26 -0700472 if (mKeyPool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800473 Logger::error(mSource)
474 << "no key string pool available for ResTable_type."
475 << std::endl;
476 return false;
477 }
478
479 const ResTable_type* type = convertTo<ResTable_type>(chunk);
480 if (!type) {
481 Logger::error(mSource)
482 << "could not parse chunk as ResTable_type."
483 << std::endl;
484 return false;
485 }
486
487 if (type->id == 0) {
488 Logger::error(mSource)
489 << "ResTable_type has invalid id: "
490 << type->id
491 << "."
492 << std::endl;
493 return false;
494 }
495
496 const ConfigDescription config(type->config);
497 const StringPiece16 typeName = util::getString(mTypePool, type->id - 1);
498
499 const ResourceType* parsedType = parseResourceType(typeName);
500 if (!parsedType) {
501 Logger::error(mSource)
502 << "invalid type name '"
503 << typeName
504 << "' for type with ID "
505 << uint32_t(type->id)
506 << "." << std::endl;
507 return false;
508 }
509
510 android::TypeVariant tv(type);
511 for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
512 if (!*it) {
513 continue;
514 }
515
516 const ResTable_entry* entry = *it;
517 const ResourceName name = {
518 mTable->getPackage(),
519 *parsedType,
520 util::getString(mKeyPool, entry->key.index).toString()
521 };
522
523 const ResourceId resId = { mTable->getPackageId(), type->id, it.index() };
524
525 std::unique_ptr<Value> resourceValue;
526 const ResTable_entry_source* sourceBlock = nullptr;
527 if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
528 const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
529 if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
530 const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry);
531 data += mapEntry->size - sizeof(*sourceBlock);
532 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
533 }
534
535 // TODO(adamlesinski): Check that the entry count is valid.
536 resourceValue = parseMapEntry(name, config, mapEntry);
537 } else {
538 if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) {
539 const uint8_t* data = reinterpret_cast<const uint8_t*>(entry);
540 data += entry->size - sizeof(*sourceBlock);
541 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
542 }
543
544 const Res_value* value = reinterpret_cast<const Res_value*>(
545 reinterpret_cast<const uint8_t*>(entry) + entry->size);
546 resourceValue = parseValue(name, config, value, entry->flags);
547 }
548
549 if (!resourceValue) {
550 // TODO(adamlesinski): For now this is ok, but it really shouldn't be.
551 continue;
552 }
553
554 SourceLine source = mSource.line(0);
555 if (sourceBlock) {
556 size_t len;
557 const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len);
558 if (str) {
559 source.path.assign(str, len);
560 }
561 source.line = sourceBlock->line;
562 }
563
564 if (!mTable->addResource(name, config, source, std::move(resourceValue))) {
565 return false;
566 }
567
568 if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
569 if (!mTable->markPublic(name, resId, mSource.line(0))) {
570 return false;
571 }
572 }
573
574 // Add this resource name->id mapping to the index so
575 // that we can resolve all ID references to name references.
576 auto cacheIter = mIdIndex.find(resId);
577 if (cacheIter == mIdIndex.end()) {
578 mIdIndex.insert({ resId, name });
579 }
580 }
581 return true;
582}
583
584std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
585 const ConfigDescription& config,
586 const Res_value* value,
587 uint16_t flags) {
588 if (value->dataType == Res_value::TYPE_STRING) {
589 StringPiece16 str = util::getString(mValuePool, value->data);
590
591 const ResStringPool_span* spans = mValuePool.styleAt(value->data);
592 if (spans != nullptr) {
593 StyleString styleStr = { str.toString() };
594 while (spans->name.index != ResStringPool_span::END) {
595 styleStr.spans.push_back(Span{
596 util::getString(mValuePool, spans->name.index).toString(),
597 spans->firstChar,
598 spans->lastChar
599 });
600 spans++;
601 }
602 return util::make_unique<StyledString>(
603 mTable->getValueStringPool().makeRef(
604 styleStr, StringPool::Context{1, config}));
605 } else {
Adam Lesinskid5c4f872015-04-21 13:56:10 -0700606 if (name.type != ResourceType::kString &&
607 util::stringStartsWith<char16_t>(str, u"res/")) {
608 // This must be a FileReference.
609 return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef(
610 str, StringPool::Context{ 0, config }));
611 }
612
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800613 // There are no styles associated with this string, so treat it as
614 // a simple string.
615 return util::make_unique<String>(
616 mTable->getValueStringPool().makeRef(
617 str, StringPool::Context{1, config}));
618 }
619 }
620
621 if (value->dataType == Res_value::TYPE_REFERENCE ||
622 value->dataType == Res_value::TYPE_ATTRIBUTE) {
623 const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
624 Reference::Type::kResource : Reference::Type::kAttribute;
625
626 if (value->data != 0) {
627 // This is a normal reference.
628 return util::make_unique<Reference>(value->data, type);
629 }
630
631 // This reference has an invalid ID. Check if it is an unresolved symbol.
632 ResourceNameRef symbol;
633 if (getSymbol(&value->data, &symbol)) {
634 return util::make_unique<Reference>(symbol, type);
635 }
636
637 // This is not an unresolved symbol, so it must be the magic @null reference.
638 Res_value nullType = {};
639 nullType.dataType = Res_value::TYPE_NULL;
640 nullType.data = Res_value::DATA_NULL_UNDEFINED;
641 return util::make_unique<BinaryPrimitive>(nullType);
642 }
643
644 if (value->dataType == ExtendedTypes::TYPE_SENTINEL) {
645 return util::make_unique<Sentinel>();
646 }
647
648 if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
649 return util::make_unique<RawString>(
650 mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data),
651 StringPool::Context{ 1, config }));
652 }
653
654 if (name.type == ResourceType::kId ||
655 (value->dataType == Res_value::TYPE_NULL &&
656 value->data == Res_value::DATA_NULL_UNDEFINED &&
657 (flags & ResTable_entry::FLAG_WEAK) != 0)) {
658 return util::make_unique<Id>();
659 }
660
661 // Treat this as a raw binary primitive.
662 return util::make_unique<BinaryPrimitive>(*value);
663}
664
665std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
666 const ConfigDescription& config,
667 const ResTable_map_entry* map) {
668 switch (name.type) {
669 case ResourceType::kStyle:
670 return parseStyle(name, config, map);
671 case ResourceType::kAttr:
672 return parseAttr(name, config, map);
673 case ResourceType::kArray:
674 return parseArray(name, config, map);
675 case ResourceType::kStyleable:
676 return parseStyleable(name, config, map);
677 case ResourceType::kPlurals:
678 return parsePlural(name, config, map);
679 default:
680 break;
681 }
682 return {};
683}
684
685std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
686 const ConfigDescription& config,
687 const ResTable_map_entry* map) {
Adam Lesinski769de982015-04-10 19:43:55 -0700688 const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
689 std::unique_ptr<Style> style = util::make_unique<Style>(isWeak);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800690 if (map->parent.ident == 0) {
691 // The parent is either not set or it is an unresolved symbol.
692 // Check to see if it is a symbol.
693 ResourceNameRef symbol;
694 if (getSymbol(&map->parent.ident, &symbol)) {
695 style->parent.name = symbol.toResourceName();
696 }
697 } else {
698 // The parent is a regular reference to a resource.
699 style->parent.id = map->parent.ident;
700 }
701
702 for (const ResTable_map& mapEntry : map) {
703 style->entries.emplace_back();
704 Style::Entry& styleEntry = style->entries.back();
705
706 if (mapEntry.name.ident == 0) {
707 // The map entry's key (attribute) is not set. This must be
708 // a symbol reference, so resolve it.
709 ResourceNameRef symbol;
710 bool result = getSymbol(&mapEntry.name.ident, &symbol);
711 assert(result);
712 styleEntry.key.name = symbol.toResourceName();
713 } else {
714 // The map entry's key (attribute) is a regular reference.
715 styleEntry.key.id = mapEntry.name.ident;
716 }
717
718 // Parse the attribute's value.
719 styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
720 assert(styleEntry.value);
721 }
722 return style;
723}
724
725std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
726 const ConfigDescription& config,
727 const ResTable_map_entry* map) {
728 const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
729 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
730
731 // First we must discover what type of attribute this is. Find the type mask.
732 auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
733 return entry.name.ident == ResTable_map::ATTR_TYPE;
734 });
735
736 if (typeMaskIter != end(map)) {
737 attr->typeMask = typeMaskIter->value.data;
738 }
739
740 if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
741 for (const ResTable_map& mapEntry : map) {
742 if (Res_INTERNALID(mapEntry.name.ident)) {
743 continue;
744 }
745
746 attr->symbols.push_back(Attribute::Symbol{
747 Reference(mapEntry.name.ident),
748 mapEntry.value.data
749 });
750 }
751 }
752
753 // TODO(adamlesinski): Find min, max, i80n, etc attributes.
754 return attr;
755}
756
757std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
758 const ConfigDescription& config,
759 const ResTable_map_entry* map) {
760 std::unique_ptr<Array> array = util::make_unique<Array>();
761 for (const ResTable_map& mapEntry : map) {
762 array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
763 }
764 return array;
765}
766
767std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
768 const ConfigDescription& config,
769 const ResTable_map_entry* map) {
770 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
771 for (const ResTable_map& mapEntry : map) {
Adam Lesinski769de982015-04-10 19:43:55 -0700772 if (mapEntry.name.ident == 0) {
773 // The map entry's key (attribute) is not set. This must be
774 // a symbol reference, so resolve it.
775 ResourceNameRef symbol;
776 bool result = getSymbol(&mapEntry.name.ident, &symbol);
777 assert(result);
778 styleable->entries.emplace_back(symbol);
779 } else {
780 // The map entry's key (attribute) is a regular reference.
781 styleable->entries.emplace_back(mapEntry.name.ident);
782 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800783 }
784 return styleable;
785}
786
787std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
788 const ConfigDescription& config,
789 const ResTable_map_entry* map) {
790 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
791 for (const ResTable_map& mapEntry : map) {
792 std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
793
794 switch (mapEntry.name.ident) {
795 case android::ResTable_map::ATTR_ZERO:
796 plural->values[Plural::Zero] = std::move(item);
797 break;
798 case android::ResTable_map::ATTR_ONE:
799 plural->values[Plural::One] = std::move(item);
800 break;
801 case android::ResTable_map::ATTR_TWO:
802 plural->values[Plural::Two] = std::move(item);
803 break;
804 case android::ResTable_map::ATTR_FEW:
805 plural->values[Plural::Few] = std::move(item);
806 break;
807 case android::ResTable_map::ATTR_MANY:
808 plural->values[Plural::Many] = std::move(item);
809 break;
810 case android::ResTable_map::ATTR_OTHER:
811 plural->values[Plural::Other] = std::move(item);
812 break;
813 }
814 }
815 return plural;
816}
817
818} // namespace aapt