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