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