summaryrefslogtreecommitdiffstats
path: root/tools/aapt2/BinaryResourceParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/BinaryResourceParser.cpp')
-rw-r--r--tools/aapt2/BinaryResourceParser.cpp794
1 files changed, 794 insertions, 0 deletions
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
new file mode 100644
index 0000000..d58f05a
--- /dev/null
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BinaryResourceParser.h"
+#include "Logger.h"
+#include "ResChunkPullParser.h"
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceTypeExtensions.h"
+#include "ResourceValues.h"
+#include "Source.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <map>
+#include <string>
+
+namespace aapt {
+
+using namespace android;
+
+template <typename T>
+inline static const T* convertTo(const ResChunk_header* chunk) {
+ if (chunk->headerSize < sizeof(T)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const T*>(chunk);
+}
+
+inline static const uint8_t* getChunkData(const ResChunk_header& chunk) {
+ return reinterpret_cast<const uint8_t*>(&chunk) + chunk.headerSize;
+}
+
+inline static size_t getChunkDataLen(const ResChunk_header& chunk) {
+ return chunk.size - chunk.headerSize;
+}
+
+/*
+ * Visitor that converts a reference's resource ID to a resource name,
+ * given a mapping from resource ID to resource name.
+ */
+struct ReferenceIdToNameVisitor : ValueVisitor {
+ ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>& cache) : mCache(cache) {
+ }
+
+ void visit(Reference& reference, ValueVisitorArgs&) override {
+ idToName(reference);
+ }
+
+ void visit(Attribute& attr, ValueVisitorArgs&) override {
+ for (auto& entry : attr.symbols) {
+ idToName(entry.symbol);
+ }
+ }
+
+ void visit(Style& style, ValueVisitorArgs&) override {
+ if (style.parent.id.isValid()) {
+ idToName(style.parent);
+ }
+
+ for (auto& entry : style.entries) {
+ idToName(entry.key);
+ entry.value->accept(*this, {});
+ }
+ }
+
+ void visit(Styleable& styleable, ValueVisitorArgs&) override {
+ for (auto& attr : styleable.entries) {
+ idToName(attr);
+ }
+ }
+
+ void visit(Array& array, ValueVisitorArgs&) override {
+ for (auto& item : array.items) {
+ item->accept(*this, {});
+ }
+ }
+
+ void visit(Plural& plural, ValueVisitorArgs&) override {
+ for (auto& item : plural.values) {
+ if (item) {
+ item->accept(*this, {});
+ }
+ }
+ }
+
+private:
+ void idToName(Reference& reference) {
+ if (!reference.id.isValid()) {
+ return;
+ }
+
+ auto cacheIter = mCache.find(reference.id);
+ if (cacheIter == std::end(mCache)) {
+ Logger::note() << "failed to find " << reference.id << std::endl;
+ } else {
+ reference.name = cacheIter->second;
+ reference.id = 0;
+ }
+ }
+
+ const std::map<ResourceId, ResourceName>& mCache;
+};
+
+
+BinaryResourceParser::BinaryResourceParser(std::shared_ptr<ResourceTable> table,
+ const Source& source,
+ const void* data,
+ size_t len) :
+ mTable(table), mSource(source), mData(data), mDataLen(len) {
+}
+
+bool BinaryResourceParser::parse() {
+ ResChunkPullParser parser(mData, mDataLen);
+
+ bool error = false;
+ while(ResChunkPullParser::isGoodEvent(parser.next())) {
+ if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
+ Logger::warn(mSource)
+ << "unknown chunk of type '"
+ << parser.getChunk()->type
+ << "'."
+ << std::endl;
+ continue;
+ }
+
+ error |= !parseTable(parser.getChunk());
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ Logger::error(mSource)
+ << "bad document: "
+ << parser.getLastError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ return !error;
+}
+
+bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
+ if (!mSymbolEntries || mSymbolEntryCount == 0) {
+ return false;
+ }
+
+ // We only support 32 bit offsets right now.
+ const ptrdiff_t offset = reinterpret_cast<uintptr_t>(data) -
+ reinterpret_cast<uintptr_t>(mData);
+ if (offset > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < mSymbolEntryCount; i++) {
+ if (mSymbolEntries[i].offset == offset) {
+ // This offset is a symbol!
+ const StringPiece16 str = util::getString(mSymbolPool,
+ mSymbolEntries[i].stringIndex);
+ StringPiece16 typeStr;
+ ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr,
+ &outSymbol->entry);
+ const ResourceType* type = parseResourceType(typeStr);
+ if (!type) {
+ return false;
+ }
+ outSymbol->type = *type;
+
+ // Since we scan the symbol table in order, we can start looking for the
+ // next symbol from this point.
+ mSymbolEntryCount -= i + 1;
+ mSymbolEntries += i + 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
+ const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk);
+ if (!symbolTableHeader) {
+ Logger::error(mSource)
+ << "could not parse chunk as SymbolTable_header."
+ << std::endl;
+ return false;
+ }
+
+ const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry);
+ if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) {
+ Logger::error(mSource)
+ << "entries extend beyond chunk."
+ << std::endl;
+ return false;
+ }
+
+ mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>(
+ getChunkData(symbolTableHeader->header));
+ mSymbolEntryCount = symbolTableHeader->count;
+
+ ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes,
+ getChunkDataLen(symbolTableHeader->header) - entrySizeBytes);
+ if (!ResChunkPullParser::isGoodEvent(parser.next())) {
+ Logger::error(mSource)
+ << "failed to parse chunk: "
+ << parser.getLastError()
+ << "."
+ << std::endl;
+ return false;
+ }
+
+ if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) {
+ Logger::error(mSource)
+ << "expected Symbol string pool."
+ << std::endl;
+ return false;
+ }
+
+ if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "failed to parse symbol string pool with code: "
+ << mSymbolPool.getError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
+ const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
+ if (!tableHeader) {
+ Logger::error(mSource)
+ << "could not parse chunk as ResTable_header."
+ << std::endl;
+ return false;
+ }
+
+ ResChunkPullParser parser(getChunkData(tableHeader->header),
+ getChunkDataLen(tableHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (parser.getChunk()->type) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mValuePool.getError() == android::NO_INIT) {
+ if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+ android::NO_ERROR) {
+ Logger::error(mSource)
+ << "failed to parse value string pool with code: "
+ << mValuePool.getError()
+ << "."
+ << std::endl;
+ return false;
+ }
+
+ // Reserve some space for the strings we are going to add.
+ mTable->getValueStringPool().hintWillAdd(
+ mValuePool.size(), mValuePool.styleCount());
+ } else {
+ Logger::warn(mSource)
+ << "unexpected string pool."
+ << std::endl;
+ }
+ break;
+
+ case RES_TABLE_SYMBOL_TABLE_TYPE:
+ if (!parseSymbolTable(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ case RES_TABLE_SOURCE_POOL_TYPE: {
+ if (mSourcePool.setTo(getChunkData(*parser.getChunk()),
+ getChunkDataLen(*parser.getChunk())) != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "failed to parse source pool with code: "
+ << mSourcePool.getError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ break;
+ }
+
+ case android::RES_TABLE_PACKAGE_TYPE:
+ if (!parsePackage(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ Logger::warn(mSource)
+ << "unexpected chunk of type "
+ << parser.getChunk()->type
+ << "."
+ << std::endl;
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ Logger::error(mSource)
+ << "bad resource table: " << parser.getLastError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
+ if (mValuePool.getError() != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "no value string pool for ResTable."
+ << std::endl;
+ return false;
+ }
+
+ const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
+ if (!packageHeader) {
+ Logger::error(mSource)
+ << "could not parse chunk as ResTable_header."
+ << std::endl;
+ return false;
+ }
+
+ if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) {
+ // This is the first time the table has it's package ID set.
+ mTable->setPackageId(packageHeader->id);
+ } else if (mTable->getPackageId() != packageHeader->id) {
+ Logger::error(mSource)
+ << "ResTable_package has package ID "
+ << std::hex << packageHeader->id << std::dec
+ << " but ResourceTable has package ID "
+ << std::hex << mTable->getPackageId() << std::dec
+ << std::endl;
+ return false;
+ }
+
+ size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name),
+ sizeof(packageHeader->name) / sizeof(packageHeader->name[0]));
+ mTable->setPackage(StringPiece16(reinterpret_cast<const char16_t*>(packageHeader->name), len));
+
+ ResChunkPullParser parser(getChunkData(packageHeader->header),
+ getChunkDataLen(packageHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (parser.getChunk()->type) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mTypePool.getError() == android::NO_INIT) {
+ if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+ android::NO_ERROR) {
+ Logger::error(mSource)
+ << "failed to parse type string pool with code "
+ << mTypePool.getError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ } else if (mKeyPool.getError() == android::NO_INIT) {
+ if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+ android::NO_ERROR) {
+ Logger::error(mSource)
+ << "failed to parse key string pool with code "
+ << mKeyPool.getError()
+ << "."
+ << std::endl;
+ return false;
+ }
+ } else {
+ Logger::warn(mSource)
+ << "unexpected string pool."
+ << std::endl;
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_SPEC_TYPE:
+ if (!parseTypeSpec(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ case android::RES_TABLE_TYPE_TYPE:
+ if (!parseType(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ Logger::warn(mSource)
+ << "unexpected chunk of type "
+ << parser.getChunk()->type
+ << "."
+ << std::endl;
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ Logger::error(mSource)
+ << "bad package: "
+ << parser.getLastError()
+ << "."
+ << std::endl;
+ return false;
+ }
+
+ // Now go through the table and change resource ID references to
+ // symbolic references.
+
+ ReferenceIdToNameVisitor visitor(mIdIndex);
+ for (auto& type : *mTable) {
+ for (auto& entry : type->entries) {
+ for (auto& configValue : entry->values) {
+ configValue.value->accept(visitor, {});
+ }
+ }
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
+ if (mTypePool.getError() != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "no type string pool available for ResTable_typeSpec."
+ << std::endl;
+ return false;
+ }
+
+ const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
+ if (!typeSpec) {
+ Logger::error(mSource)
+ << "could not parse chunk as ResTable_typeSpec."
+ << std::endl;
+ return false;
+ }
+
+ if (typeSpec->id == 0) {
+ Logger::error(mSource)
+ << "ResTable_typeSpec has invalid id: "
+ << typeSpec->id
+ << "."
+ << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
+ if (mTypePool.getError() != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "no type string pool available for ResTable_typeSpec."
+ << std::endl;
+ return false;
+ }
+
+ if (mKeyPool.getError() != android::NO_ERROR) {
+ Logger::error(mSource)
+ << "no key string pool available for ResTable_type."
+ << std::endl;
+ return false;
+ }
+
+ const ResTable_type* type = convertTo<ResTable_type>(chunk);
+ if (!type) {
+ Logger::error(mSource)
+ << "could not parse chunk as ResTable_type."
+ << std::endl;
+ return false;
+ }
+
+ if (type->id == 0) {
+ Logger::error(mSource)
+ << "ResTable_type has invalid id: "
+ << type->id
+ << "."
+ << std::endl;
+ return false;
+ }
+
+ const ConfigDescription config(type->config);
+ const StringPiece16 typeName = util::getString(mTypePool, type->id - 1);
+
+ const ResourceType* parsedType = parseResourceType(typeName);
+ if (!parsedType) {
+ Logger::error(mSource)
+ << "invalid type name '"
+ << typeName
+ << "' for type with ID "
+ << uint32_t(type->id)
+ << "." << std::endl;
+ return false;
+ }
+
+ android::TypeVariant tv(type);
+ for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+ if (!*it) {
+ continue;
+ }
+
+ const ResTable_entry* entry = *it;
+ const ResourceName name = {
+ mTable->getPackage(),
+ *parsedType,
+ util::getString(mKeyPool, entry->key.index).toString()
+ };
+
+ const ResourceId resId = { mTable->getPackageId(), type->id, it.index() };
+
+ std::unique_ptr<Value> resourceValue;
+ const ResTable_entry_source* sourceBlock = nullptr;
+ if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
+ if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry);
+ data += mapEntry->size - sizeof(*sourceBlock);
+ sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
+ }
+
+ // TODO(adamlesinski): Check that the entry count is valid.
+ resourceValue = parseMapEntry(name, config, mapEntry);
+ } else {
+ if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(entry);
+ data += entry->size - sizeof(*sourceBlock);
+ sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
+ }
+
+ const Res_value* value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry) + entry->size);
+ resourceValue = parseValue(name, config, value, entry->flags);
+ }
+
+ if (!resourceValue) {
+ // TODO(adamlesinski): For now this is ok, but it really shouldn't be.
+ continue;
+ }
+
+ SourceLine source = mSource.line(0);
+ if (sourceBlock) {
+ size_t len;
+ const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len);
+ if (str) {
+ source.path.assign(str, len);
+ }
+ source.line = sourceBlock->line;
+ }
+
+ if (!mTable->addResource(name, config, source, std::move(resourceValue))) {
+ return false;
+ }
+
+ if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
+ if (!mTable->markPublic(name, resId, mSource.line(0))) {
+ return false;
+ }
+ }
+
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cacheIter = mIdIndex.find(resId);
+ if (cacheIter == mIdIndex.end()) {
+ mIdIndex.insert({ resId, name });
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Res_value* value,
+ uint16_t flags) {
+ if (value->dataType == Res_value::TYPE_STRING) {
+ StringPiece16 str = util::getString(mValuePool, value->data);
+
+ const ResStringPool_span* spans = mValuePool.styleAt(value->data);
+ if (spans != nullptr) {
+ StyleString styleStr = { str.toString() };
+ while (spans->name.index != ResStringPool_span::END) {
+ styleStr.spans.push_back(Span{
+ util::getString(mValuePool, spans->name.index).toString(),
+ spans->firstChar,
+ spans->lastChar
+ });
+ spans++;
+ }
+ return util::make_unique<StyledString>(
+ mTable->getValueStringPool().makeRef(
+ styleStr, StringPool::Context{1, config}));
+ } else {
+ // There are no styles associated with this string, so treat it as
+ // a simple string.
+ return util::make_unique<String>(
+ mTable->getValueStringPool().makeRef(
+ str, StringPool::Context{1, config}));
+ }
+ }
+
+ if (value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) {
+ const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
+ Reference::Type::kResource : Reference::Type::kAttribute;
+
+ if (value->data != 0) {
+ // This is a normal reference.
+ return util::make_unique<Reference>(value->data, type);
+ }
+
+ // This reference has an invalid ID. Check if it is an unresolved symbol.
+ ResourceNameRef symbol;
+ if (getSymbol(&value->data, &symbol)) {
+ return util::make_unique<Reference>(symbol, type);
+ }
+
+ // This is not an unresolved symbol, so it must be the magic @null reference.
+ Res_value nullType = {};
+ nullType.dataType = Res_value::TYPE_NULL;
+ nullType.data = Res_value::DATA_NULL_UNDEFINED;
+ return util::make_unique<BinaryPrimitive>(nullType);
+ }
+
+ if (value->dataType == ExtendedTypes::TYPE_SENTINEL) {
+ return util::make_unique<Sentinel>();
+ }
+
+ if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
+ return util::make_unique<RawString>(
+ mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data),
+ StringPool::Context{ 1, config }));
+ }
+
+ if (name.type == ResourceType::kId ||
+ (value->dataType == Res_value::TYPE_NULL &&
+ value->data == Res_value::DATA_NULL_UNDEFINED &&
+ (flags & ResTable_entry::FLAG_WEAK) != 0)) {
+ return util::make_unique<Id>();
+ }
+
+ // Treat this as a raw binary primitive.
+ return util::make_unique<BinaryPrimitive>(*value);
+}
+
+std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ switch (name.type) {
+ case ResourceType::kStyle:
+ return parseStyle(name, config, map);
+ case ResourceType::kAttr:
+ return parseAttr(name, config, map);
+ case ResourceType::kArray:
+ return parseArray(name, config, map);
+ case ResourceType::kStyleable:
+ return parseStyleable(name, config, map);
+ case ResourceType::kPlurals:
+ return parsePlural(name, config, map);
+ default:
+ break;
+ }
+ return {};
+}
+
+std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (map->parent.ident == 0) {
+ // The parent is either not set or it is an unresolved symbol.
+ // Check to see if it is a symbol.
+ ResourceNameRef symbol;
+ if (getSymbol(&map->parent.ident, &symbol)) {
+ style->parent.name = symbol.toResourceName();
+ }
+ } else {
+ // The parent is a regular reference to a resource.
+ style->parent.id = map->parent.ident;
+ }
+
+ for (const ResTable_map& mapEntry : map) {
+ style->entries.emplace_back();
+ Style::Entry& styleEntry = style->entries.back();
+
+ if (mapEntry.name.ident == 0) {
+ // The map entry's key (attribute) is not set. This must be
+ // a symbol reference, so resolve it.
+ ResourceNameRef symbol;
+ bool result = getSymbol(&mapEntry.name.ident, &symbol);
+ assert(result);
+ styleEntry.key.name = symbol.toResourceName();
+ } else {
+ // The map entry's key (attribute) is a regular reference.
+ styleEntry.key.id = mapEntry.name.ident;
+ }
+
+ // Parse the attribute's value.
+ styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
+ assert(styleEntry.value);
+ }
+ return style;
+}
+
+std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+
+ // First we must discover what type of attribute this is. Find the type mask.
+ auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return entry.name.ident == ResTable_map::ATTR_TYPE;
+ });
+
+ if (typeMaskIter != end(map)) {
+ attr->typeMask = typeMaskIter->value.data;
+ }
+
+ if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(mapEntry.name.ident)) {
+ continue;
+ }
+
+ attr->symbols.push_back(Attribute::Symbol{
+ Reference(mapEntry.name.ident),
+ mapEntry.value.data
+ });
+ }
+ }
+
+ // TODO(adamlesinski): Find min, max, i80n, etc attributes.
+ return attr;
+}
+
+std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const ResTable_map& mapEntry : map) {
+ array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
+ }
+ return array;
+}
+
+std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const ResTable_map& mapEntry : map) {
+ styleable->entries.emplace_back(mapEntry.name.ident);
+ }
+ return styleable;
+}
+
+std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const ResTable_map& mapEntry : map) {
+ std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
+
+ switch (mapEntry.name.ident) {
+ case android::ResTable_map::ATTR_ZERO:
+ plural->values[Plural::Zero] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_ONE:
+ plural->values[Plural::One] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_TWO:
+ plural->values[Plural::Two] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_FEW:
+ plural->values[Plural::Few] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_MANY:
+ plural->values[Plural::Many] = std::move(item);
+ break;
+ case android::ResTable_map::ATTR_OTHER:
+ plural->values[Plural::Other] = std::move(item);
+ break;
+ }
+ }
+ return plural;
+}
+
+} // namespace aapt