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