16f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
26f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
36f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
46f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
56f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * you may not use this file except in compliance with the License.
66f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * You may obtain a copy of the License at
76f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
86f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
96f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Unless required by applicable law or agreed to in writing, software
116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * See the License for the specific language governing permissions and
146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * limitations under the License.
156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceParser.h"
181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceTable.h"
191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceUtils.h"
206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceValues.h"
211ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ValueVisitor.h"
227ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski#include "util/ImmutableMap.h"
239e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski#include "util/Util.h"
24467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
259e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
267ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski#include <functional>
27769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski#include <sstream>
28769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiconstexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2";
326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski/**
3427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski * Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
3527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski */
3627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinskistatic bool shouldIgnoreElement(const StringPiece16& ns, const StringPiece16& name) {
3727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    return ns.empty() && (name == u"skip" || name == u"eat-comment");
3827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
3927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
407ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskistatic uint32_t parseFormatType(const StringPiece16& piece) {
417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
427ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
437ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"integer")   return android::ResTable_map::TYPE_INTEGER;
447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"boolean")   return android::ResTable_map::TYPE_BOOLEAN;
457ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"color")     return android::ResTable_map::TYPE_COLOR;
467ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"float")     return android::ResTable_map::TYPE_FLOAT;
477ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION;
487ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"fraction")  return android::ResTable_map::TYPE_FRACTION;
497ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"enum")      return android::ResTable_map::TYPE_ENUM;
507ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    else if (piece == u"flags")     return android::ResTable_map::TYPE_FLAGS;
517ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return 0;
527ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
537ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
547ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskistatic uint32_t parseFormatAttribute(const StringPiece16& str) {
557ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    uint32_t mask = 0;
567ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    for (StringPiece16 part : util::tokenize(str, u'|')) {
577ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        StringPiece16 trimmedPart = util::trimWhitespace(part);
587ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        uint32_t type = parseFormatType(trimmedPart);
597ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (type == 0) {
607ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return 0;
617ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
627ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mask |= type;
637ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
647ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return mask;
657ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
667ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
677ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski/**
687ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski * A parsed resource ready to be added to the ResourceTable.
697ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski */
707ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskistruct ParsedResource {
717ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    ResourceName name;
7252364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    ConfigDescription config;
73e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski    std::string product;
747ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    Source source;
757ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    ResourceId id;
767ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    Maybe<SymbolState> symbolState;
777ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    std::u16string comment;
787ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    std::unique_ptr<Value> value;
797ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    std::list<ParsedResource> childResources;
807ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski};
817ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
827ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski// Recursively adds resources to the ResourceTable.
8352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinskistatic bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
847656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    StringPiece16 trimmedComment = util::trimWhitespace(res->comment);
857656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    if (trimmedComment.size() != res->comment.size()) {
867656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski        // Only if there was a change do we re-assign.
877656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski        res->comment = trimmedComment.toString();
887656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    }
897656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
907ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (res->symbolState) {
917ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        Symbol symbol;
927ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        symbol.state = res->symbolState.value();
937ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        symbol.source = res->source;
947ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        symbol.comment = res->comment;
957ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
967ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
977ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
987ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
997ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1007ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (res->value) {
1017ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // Attach the comment, source and config to the value.
1027ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        res->value->setComment(std::move(res->comment));
1037ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        res->value->setSource(std::move(res->source));
1047ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
105e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski        if (!table->addResource(res->name, res->id, res->config, res->product,
106e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski                                std::move(res->value), diag)) {
1077ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
1087ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
1097ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
1107ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1117ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    bool error = false;
1127ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    for (ParsedResource& child : res->childResources) {
11352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        error |= !addResourcesToTable(table, diag, &child);
1147ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
1157ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return !error;
1167ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
1177ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1187ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski// Convenient aliases for more readable function calls.
1197ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskienum {
1207ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    kAllowRawString = true,
1217ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    kNoRawString = false
1227ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski};
1237ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1241ab598f46c3ff520a67f9d80194847741f3467abAdam LesinskiResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
1259ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                               const ConfigDescription& config,
1269ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                               const ResourceParserOptions& options) :
1279ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) {
1286f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
1296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/**
1316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Build a string from XML that converts nested elements into Span objects.
1326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
133467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString,
1346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                                       StyleString* outStyleString) {
1356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::vector<Span> spanStack;
1366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
137b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    bool error = false;
1386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    outRawString->clear();
1396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    outStyleString->spans.clear();
1406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    util::StringBuilder builder;
1416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    size_t depth = 1;
142467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::isGoodEvent(parser->next())) {
143467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        const xml::XmlPullParser::Event event = parser->getEvent();
144467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (event == xml::XmlPullParser::Event::kEndElement) {
1451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (!parser->getElementNamespace().empty()) {
1461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                // We already warned and skipped the start element, so just skip here too
1471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
1481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
1491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            depth--;
1516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            if (depth == 0) {
1526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                break;
1536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
1546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            spanStack.back().lastChar = builder.str().size();
1566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            outStyleString->spans.push_back(spanStack.back());
1576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            spanStack.pop_back();
1586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
159467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (event == xml::XmlPullParser::Event::kText) {
1606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            outRawString->append(parser->getText());
1616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            builder.append(parser->getText());
1626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
163467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (event == xml::XmlPullParser::Event::kStartElement) {
1641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (!parser->getElementNamespace().empty()) {
1651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                if (parser->getElementNamespace() != sXliffNamespaceUri) {
1661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    // Only warn if this isn't an xliff namespace.
1671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
1681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                << "skipping element '"
1691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                << parser->getElementName()
1701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                << "' with unknown namespace '"
1711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                << parser->getElementNamespace()
1721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                << "'");
1731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                }
1746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                continue;
1756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
1766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            depth++;
1776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            // Build a span object out of the nested element.
1796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            std::u16string spanName = parser->getElementName();
1806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            const auto endAttrIter = parser->endAttributes();
1816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
1826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                spanName += u";";
1836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                spanName += attrIter->name;
1846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                spanName += u"=";
1856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                spanName += attrIter->value;
1866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
1876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            if (builder.str().size() > std::numeric_limits<uint32_t>::max()) {
1891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
1901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "style string '" << builder.str() << "' is too long");
191b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski                error = true;
192b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski            } else {
193b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski                spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
1946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
1956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
196467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (event == xml::XmlPullParser::Event::kComment) {
1976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            // Skip
1986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        } else {
1991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            assert(false);
2006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
2026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    assert(spanStack.empty() && "spans haven't been fully processed");
2036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    outStyleString->str = builder.str();
205b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    return !error;
2066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
208467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parse(xml::XmlPullParser* parser) {
2091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
2101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
211467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
212467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
2131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            // Skip comments and text.
2146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
2156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!parser->getElementNamespace().empty() || parser->getElementName() != u"resources") {
2181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
2191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << "root element must be <resources>");
2206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            return false;
2216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        error |= !parseResources(parser);
2241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        break;
2251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    };
2266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
227467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
2281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
2291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                     << "xml parser error: " << parser->getLastError());
2306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
2316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
2321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    return !error;
2336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
235467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseResources(xml::XmlPullParser* parser) {
2369ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    std::set<ResourceName> strippedResources;
2379ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
2381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
2396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::u16string comment;
2401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
241467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
242467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        const xml::XmlPullParser::Event event = parser->getEvent();
243467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (event == xml::XmlPullParser::Event::kComment) {
2446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            comment = parser->getComment();
2456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
2466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
248467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (event == xml::XmlPullParser::Event::kText) {
2496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            if (!util::trimWhitespace(parser->getText()).empty()) {
2501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
2511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "plain text not allowed here");
2521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
2536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
2546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
2556f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
257467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        assert(event == xml::XmlPullParser::Event::kStartElement);
2586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!parser->getElementNamespace().empty()) {
2606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            // Skip unknown namespace.
2616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
2626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        std::u16string elementName = parser->getElementName();
2651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (elementName == u"skip" || elementName == u"eat-comment") {
2661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            comment = u"";
2676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
2686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2709ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        ParsedResource parsedResource;
27152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        parsedResource.config = mConfig;
2729ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        parsedResource.source = mSource.withLine(parser->getLineNumber());
273e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski        parsedResource.comment = std::move(comment);
2741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
2757751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        // Extract the product name if it exists.
2767751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
277e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski            parsedResource.product = util::utf16ToUtf8(maybeProduct.value());
2787751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        }
27927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
2807751afc796842bbb24bfbb19bd0fee4a7b7c8a4eAdam Lesinski        // Parse the resource regardless of product.
2817ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!parseResource(parser, &parsedResource)) {
28227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            error = true;
28327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            continue;
28427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        }
28527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
286e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski        if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
2879ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            error = true;
2889ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        }
2896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
2909ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
2919ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    // Check that we included at least one variant of each stripped resource.
2929ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    for (const ResourceName& strippedResource : strippedResources) {
2939ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        if (!mTable->findResource(strippedResource)) {
2949ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            // Failed to find the resource.
2959ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' "
2969ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                         "was filtered out but no product variant remains");
2979ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            error = true;
2989ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        }
2999ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    }
3009ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
3011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    return !error;
3026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
3036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
3047ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3057ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
3067ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    struct ItemTypeFormat {
3077ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        ResourceType type;
3087ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        uint32_t format;
3097ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    };
3107ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3117ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>;
3127ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3137ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    static const auto elToItemMap = ImmutableMap<std::u16string, ItemTypeFormat>::createPreSorted({
3147ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"bool",      { ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN } },
3157ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"color",     { ResourceType::kColor, android::ResTable_map::TYPE_COLOR } },
3167ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"dimen",     { ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT
3177ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                                    | android::ResTable_map::TYPE_FRACTION
3187ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                                    | android::ResTable_map::TYPE_DIMENSION } },
3197ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"drawable",  { ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR } },
3207ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"fraction",  { ResourceType::kFraction, android::ResTable_map::TYPE_FLOAT
3217ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                                       | android::ResTable_map::TYPE_FRACTION
3227ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                                       | android::ResTable_map::TYPE_DIMENSION } },
3237ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"integer",   { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } },
3247ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"string",    { ResourceType::kString, android::ResTable_map::TYPE_STRING } },
3257ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    });
3267ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3277ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    static const auto elToBagMap = ImmutableMap<std::u16string, BagParseFunc>::createPreSorted({
3287ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"add-resource",      std::mem_fn(&ResourceParser::parseAddResource) },
3297ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"array",             std::mem_fn(&ResourceParser::parseArray) },
3307ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"attr",              std::mem_fn(&ResourceParser::parseAttr) },
3317ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"declare-styleable", std::mem_fn(&ResourceParser::parseDeclareStyleable) },
3327ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"integer-array",     std::mem_fn(&ResourceParser::parseIntegerArray) },
3337ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"java-symbol",       std::mem_fn(&ResourceParser::parseSymbol) },
3347ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"plurals",           std::mem_fn(&ResourceParser::parsePlural) },
3357ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"public",            std::mem_fn(&ResourceParser::parsePublic) },
3367ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"public-group",      std::mem_fn(&ResourceParser::parsePublicGroup) },
3377ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"string-array",      std::mem_fn(&ResourceParser::parseStringArray) },
3387ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"style",             std::mem_fn(&ResourceParser::parseStyle) },
3397ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            { u"symbol",            std::mem_fn(&ResourceParser::parseSymbol) },
3407ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    });
3417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3427ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    std::u16string resourceType = parser->getElementName();
3437ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // The value format accepted for this resource.
3457ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    uint32_t resourceFormat = 0u;
3467ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3477ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (resourceType == u"item") {
3487ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // Items have their type encoded in the type attribute.
3497ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) {
3507ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            resourceType = maybeType.value().toString();
3517ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        } else {
3527ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
3537ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "<item> must have a 'type' attribute");
3547ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
3557ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
3567ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3577ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (Maybe<StringPiece16> maybeFormat = xml::findNonEmptyAttribute(parser, u"format")) {
3587ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            // An explicit format for this resource was specified. The resource will retain
3597ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            // its type in its name, but the accepted value for this type is overridden.
3607ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            resourceFormat = parseFormatType(maybeFormat.value());
3617ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            if (!resourceFormat) {
3627ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                mDiag->error(DiagMessage(outResource->source)
3637ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                             << "'" << maybeFormat.value() << "' is an invalid format");
3647ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                return false;
3657ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            }
3667ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
3677ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
3687ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3697ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // Get the name of the resource. This will be checked later, because not all
3707ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // XML elements require a name.
3717ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
3727ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3737ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (resourceType == u"id") {
3747ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!maybeName) {
3757ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
3767ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "<" << parser->getElementName() << "> missing 'name' attribute");
3777ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
3787ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
3797ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3807ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.type = ResourceType::kId;
3817ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.entry = maybeName.value().toString();
3827ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->value = util::make_unique<Id>();
3837ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return true;
3847ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
3857ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3867ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    const auto itemIter = elToItemMap.find(resourceType);
3877ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (itemIter != elToItemMap.end()) {
3887ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // This is an item, record its type and format and start parsing.
3897ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3907ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!maybeName) {
3917ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
3927ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "<" << parser->getElementName() << "> missing 'name' attribute");
3937ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
3947ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
3957ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3967ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.type = itemIter->second.type;
3977ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.entry = maybeName.value().toString();
3987ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
3997ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // Only use the implicit format for this type if it wasn't overridden.
4007ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!resourceFormat) {
4017ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            resourceFormat = itemIter->second.format;
4027ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4037ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4047ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!parseItem(parser, outResource, resourceFormat)) {
4057ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
4067ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4077ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return true;
4087ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
4097ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4107ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // This might be a bag or something.
4117ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    const auto bagIter = elToBagMap.find(resourceType);
4127ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (bagIter != elToBagMap.end()) {
4137ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // Ensure we have a name (unless this is a <public-group>).
4147ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (resourceType != u"public-group") {
4157ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            if (!maybeName) {
4167ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                mDiag->error(DiagMessage(outResource->source)
4177ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                             << "<" << parser->getElementName() << "> missing 'name' attribute");
4187ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                return false;
4197ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            }
4207ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4217ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            outResource->name.entry = maybeName.value().toString();
4227ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4237ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4247ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // Call the associated parse method. The type will be filled in by the
4257ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        // parse func.
4267ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!bagIter->second(this, parser, outResource)) {
4277ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
4287ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4297ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return true;
4307ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
4317ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4327ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // Try parsing the elementName (or type) as a resource. These shall only be
4337ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    // resources like 'layout' or 'xml' and they can only be references.
4347ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    const ResourceType* parsedType = parseResourceType(resourceType);
4357ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (parsedType) {
4367ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!maybeName) {
4377ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
4387ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "<" << parser->getElementName() << "> missing 'name' attribute");
4397ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
4407ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4427ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.type = *parsedType;
4437ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->name.entry = maybeName.value().toString();
4447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        outResource->value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
4457ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        if (!outResource->value) {
4467ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
4477ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "invalid value for type '" << *parsedType << "'. Expected a reference");
4487ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            return false;
4497ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        }
4507ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return true;
4517ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
4527ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4537ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    mDiag->warn(DiagMessage(outResource->source)
4547ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                << "unknown resource type '" << parser->getElementName() << "'");
4557ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return false;
4567ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
4577ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4587ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
4597ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                               const uint32_t format) {
4607ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (format == android::ResTable_map::TYPE_STRING) {
4617ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return parseString(parser, outResource);
4627ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
4637ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
4647ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->value = parseXml(parser, format, kNoRawString);
4657ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    if (!outResource->value) {
4667ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source) << "invalid " << outResource->name.type);
4677ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        return false;
4687ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
4697ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return true;
4707ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
4716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/**
4736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Reads the entire XML subtree and attempts to parse it as some Item,
4746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * with typeMask denoting which items it can be. If allowRawValue is
4756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * true, a RawString is returned if the XML couldn't be parsed as
4766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * an Item. If allowRawValue is false, nullptr is returned in this
4776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * case.
4786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
479467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskistd::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
480e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski                                               const bool allowRawValue) {
4816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const size_t beginXmlLine = parser->getLineNumber();
4826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::u16string rawValue;
4846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    StyleString styleString;
4856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
4866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return {};
4876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
4886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (!styleString.spans.empty()) {
4906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        // This can only be a StyledString.
4916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return util::make_unique<StyledString>(
4921ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mTable->stringPool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
4936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
4946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
4956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    auto onCreateReference = [&](const ResourceName& name) {
49624aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski        // name.package can be empty here, as it will assume the package name of the table.
497e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski        std::unique_ptr<Id> id = util::make_unique<Id>();
498e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski        id->setSource(mSource.withLine(beginXmlLine));
499e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski        mTable->addResource(name, {}, {}, std::move(id), mDiag);
5006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    };
5016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    // Process the raw value.
5031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::unique_ptr<Item> processedItem = ResourceUtils::parseItemForAttribute(rawValue, typeMask,
5041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                                                               onCreateReference);
5056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (processedItem) {
50624aad163bc88cb10d2275385e9afc3de7f342d65Adam Lesinski        // Fix up the reference.
5071ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (Reference* ref = valueCast<Reference>(processedItem.get())) {
508467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            transformReferenceFromNamespace(parser, u"", ref);
5091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
5106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return processedItem;
5116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
5126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    // Try making a regular string.
5146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (typeMask & android::ResTable_map::TYPE_STRING) {
5156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        // Use the trimmed, escaped string.
5166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return util::make_unique<String>(
5171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mTable->stringPool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
5186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
5196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (allowRawValue) {
521e78fd617ec60139a973a01925fa7adad31febb39Adam Lesinski        // We can't parse this so return a RawString if we are allowed.
5226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return util::make_unique<RawString>(
5231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mTable->stringPool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
5246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
5256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    return {};
5266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
528467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
529b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    bool formatted = true;
530467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) {
531b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski        if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) {
5327ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
5337ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "invalid value for 'formatted'. Must be a boolean");
534b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski            return false;
535b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski        }
536b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    }
537b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski
5389f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski    bool translateable = mOptions.translatable;
539467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) {
5409f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski        if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
5417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
5429f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski                         << "invalid value for 'translatable'. Must be a boolean");
543b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski            return false;
544b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski        }
545b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    }
5461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5479ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
5489ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    if (!outResource->value) {
5497ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source) << "not a valid string");
5506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
5516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
552b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski
553393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    if (String* stringValue = valueCast<String>(outResource->value.get())) {
554393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        stringValue->setTranslateable(translateable);
555393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
556393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        if (formatted && translateable) {
557b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski            if (!util::verifyJavaStringFormat(*stringValue->value)) {
558979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                DiagMessage msg(outResource->source);
559979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                msg << "multiple substitutions specified in non-positional format; "
560979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                       "did you mean to add the formatted=\"false\" attribute?";
561979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                if (mOptions.errorOnPositionalArguments) {
562979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                    mDiag->error(msg);
563979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                    return false;
564979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                }
565979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski
566979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                mDiag->warn(msg);
567b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski            }
568b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski        }
569393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
570393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    } else if (StyledString* stringValue = valueCast<StyledString>(outResource->value.get())) {
571393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        stringValue->setTranslateable(translateable);
572b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski    }
5739ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
5746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
5756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
576467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
577467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
5781ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!maybeType) {
5797ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source) << "<public> must have a 'type' attribute");
5806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
5816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
5826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const ResourceType* parsedType = parseResourceType(maybeType.value());
5846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (!parsedType) {
5857ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
5867ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "invalid resource type '" << maybeType.value() << "' in <public>");
5876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
5886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
5896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5909ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->name.type = *parsedType;
5916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
592467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    if (Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"id")) {
5936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        android::Res_value val;
5941ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        bool result = android::ResTable::stringToInt(maybeId.value().data(),
5951ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                                     maybeId.value().size(), &val);
5969ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        ResourceId resourceId(val.data);
5976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        if (!result || !resourceId.isValid()) {
5987ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski            mDiag->error(DiagMessage(outResource->source)
5997ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                         << "invalid resource ID '" << maybeId.value() << "' in <public>");
6006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            return false;
6016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
6029ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        outResource->id = resourceId;
6036f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
6046f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
6056f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (*parsedType == ResourceType::kId) {
6066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        // An ID marked as public is also the definition of an ID.
6079ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        outResource->value = util::make_unique<Id>();
6086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
6099ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
6109e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    outResource->symbolState = SymbolState::kPublic;
6119e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    return true;
6129e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski}
6139e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
614467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
615467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
61627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    if (!maybeType) {
6177ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
6187ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "<public-group> must have a 'type' attribute");
61927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        return false;
62027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
62127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
62227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    const ResourceType* parsedType = parseResourceType(maybeType.value());
62327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    if (!parsedType) {
6247ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
6257ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "invalid resource type '" << maybeType.value() << "' in <public-group>");
62627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        return false;
62727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
62827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
629467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id");
63027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    if (!maybeId) {
6317ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
6327ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "<public-group> must have a 'first-id' attribute");
63327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        return false;
63427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
63527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
63627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    android::Res_value val;
63727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    bool result = android::ResTable::stringToInt(maybeId.value().data(),
63827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                                                 maybeId.value().size(), &val);
63927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    ResourceId nextId(val.data);
64027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    if (!result || !nextId.isValid()) {
6417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
6427ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "invalid resource ID '" << maybeId.value() << "' in <public-group>");
64327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        return false;
64427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
64527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
64627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    std::u16string comment;
64727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    bool error = false;
64827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    const size_t depth = parser->getDepth();
649467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
650467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
65127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            comment = util::trimWhitespace(parser->getComment()).toString();
65227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            continue;
653467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
65427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            // Skip text.
65527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            continue;
65627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        }
65727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
65827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        const Source itemSource = mSource.withLine(parser->getLineNumber());
65927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
66027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        const std::u16string& elementName = parser->getElementName();
66127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        if (elementNamespace.empty() && elementName == u"public") {
662467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
66327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            if (!maybeName) {
66427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute");
66527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                error = true;
66627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                continue;
66727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            }
66827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
669467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            if (xml::findNonEmptyAttribute(parser, u"id")) {
67027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
67127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                error = true;
67227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                continue;
67327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            }
67427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
675467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            if (xml::findNonEmptyAttribute(parser, u"type")) {
67627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
67727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                error = true;
67827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski                continue;
67927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            }
68027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
68127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            ParsedResource childResource;
68227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.name.type = *parsedType;
68327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.name.entry = maybeName.value().toString();
68427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.id = nextId;
68527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.comment = std::move(comment);
68627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.source = itemSource;
68727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            childResource.symbolState = SymbolState::kPublic;
68827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            outResource->childResources.push_back(std::move(childResource));
68927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
69027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            nextId.id += 1;
69127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
69227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
69327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
69427afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski            error = true;
69527afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski        }
69627afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
69727afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    return !error;
69827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
69927afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
700a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinskibool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
701467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
7029e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    if (!maybeType) {
7037ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
7047ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "<" << parser->getElementName() << "> must have a 'type' attribute");
7059e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski        return false;
7069e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    }
7079e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
7089e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    const ResourceType* parsedType = parseResourceType(maybeType.value());
7099e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    if (!parsedType) {
7107ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski        mDiag->error(DiagMessage(outResource->source)
7117ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                     << "invalid resource type '" << maybeType.value()
7129e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski                     << "' in <" << parser->getElementName() << ">");
7139e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski        return false;
7149e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    }
7159e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
7169e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    outResource->name.type = *parsedType;
7179ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
7186f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
7196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
720a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinskibool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
721a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    if (parseSymbolImpl(parser, outResource)) {
722a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski        outResource->symbolState = SymbolState::kPrivate;
723a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski        return true;
724a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    }
725a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    return false;
726a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
727a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
728a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinskibool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
729a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    if (parseSymbolImpl(parser, outResource)) {
730a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski        outResource->symbolState = SymbolState::kUndefined;
731a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski        return true;
732a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    }
733a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    return false;
734a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
735a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
7366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
737467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
7389ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return parseAttrImpl(parser, outResource, false);
7396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
7406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
741467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
742467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                                   bool weak) {
7437ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->name.type = ResourceType::kAttr;
7447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
74552364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    // Attributes only end up in default configuration.
74652364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    if (outResource->config != ConfigDescription::defaultConfig()) {
74752364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
74852364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski                    << outResource->config << "' for attribute " << outResource->name);
74952364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        outResource->config = ConfigDescription::defaultConfig();
75052364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    }
75152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
7526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    uint32_t typeMask = 0;
7536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
754467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
7551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (maybeFormat) {
7561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        typeMask = parseFormatAttribute(maybeFormat.value());
7576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        if (typeMask == 0) {
7581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
7591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << "invalid attribute format '" << maybeFormat.value() << "'");
7609ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            return false;
7616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
7626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
7636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
764a587065721053ad54e34f484868142407d59512dAdam Lesinski    Maybe<int32_t> maybeMin, maybeMax;
765a587065721053ad54e34f484868142407d59512dAdam Lesinski
766a587065721053ad54e34f484868142407d59512dAdam Lesinski    if (Maybe<StringPiece16> maybeMinStr = xml::findAttribute(parser, u"min")) {
767a587065721053ad54e34f484868142407d59512dAdam Lesinski        StringPiece16 minStr = util::trimWhitespace(maybeMinStr.value());
768a587065721053ad54e34f484868142407d59512dAdam Lesinski        if (!minStr.empty()) {
769a587065721053ad54e34f484868142407d59512dAdam Lesinski            android::Res_value value;
770a587065721053ad54e34f484868142407d59512dAdam Lesinski            if (android::ResTable::stringToInt(minStr.data(), minStr.size(), &value)) {
771a587065721053ad54e34f484868142407d59512dAdam Lesinski                maybeMin = static_cast<int32_t>(value.data);
772a587065721053ad54e34f484868142407d59512dAdam Lesinski            }
773a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
774a587065721053ad54e34f484868142407d59512dAdam Lesinski
775a587065721053ad54e34f484868142407d59512dAdam Lesinski        if (!maybeMin) {
776a587065721053ad54e34f484868142407d59512dAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
777a587065721053ad54e34f484868142407d59512dAdam Lesinski                         << "invalid 'min' value '" << minStr << "'");
778a587065721053ad54e34f484868142407d59512dAdam Lesinski            return false;
779a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
780a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
781a587065721053ad54e34f484868142407d59512dAdam Lesinski
782a587065721053ad54e34f484868142407d59512dAdam Lesinski    if (Maybe<StringPiece16> maybeMaxStr = xml::findAttribute(parser, u"max")) {
783a587065721053ad54e34f484868142407d59512dAdam Lesinski        StringPiece16 maxStr = util::trimWhitespace(maybeMaxStr.value());
784a587065721053ad54e34f484868142407d59512dAdam Lesinski        if (!maxStr.empty()) {
785a587065721053ad54e34f484868142407d59512dAdam Lesinski            android::Res_value value;
786a587065721053ad54e34f484868142407d59512dAdam Lesinski            if (android::ResTable::stringToInt(maxStr.data(), maxStr.size(), &value)) {
787a587065721053ad54e34f484868142407d59512dAdam Lesinski                maybeMax = static_cast<int32_t>(value.data);
788a587065721053ad54e34f484868142407d59512dAdam Lesinski            }
789a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
790a587065721053ad54e34f484868142407d59512dAdam Lesinski
791a587065721053ad54e34f484868142407d59512dAdam Lesinski        if (!maybeMax) {
792a587065721053ad54e34f484868142407d59512dAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
793a587065721053ad54e34f484868142407d59512dAdam Lesinski                         << "invalid 'max' value '" << maxStr << "'");
794a587065721053ad54e34f484868142407d59512dAdam Lesinski            return false;
795a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
796a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
797a587065721053ad54e34f484868142407d59512dAdam Lesinski
798a587065721053ad54e34f484868142407d59512dAdam Lesinski    if ((maybeMin || maybeMax) && (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
799a587065721053ad54e34f484868142407d59512dAdam Lesinski        mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
800a587065721053ad54e34f484868142407d59512dAdam Lesinski                     << "'min' and 'max' can only be used when format='integer'");
801a587065721053ad54e34f484868142407d59512dAdam Lesinski        return false;
802a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
803a587065721053ad54e34f484868142407d59512dAdam Lesinski
804abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski    struct SymbolComparator {
805abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski        bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
806abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski            return a.symbol.name.value() < b.symbol.name.value();
807abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski        }
808abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski    };
809abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski
810abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski    std::set<Attribute::Symbol, SymbolComparator> items;
8116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
8121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::u16string comment;
8136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool error = false;
8141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
815467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
816467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
817ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            comment = util::trimWhitespace(parser->getComment()).toString();
818ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            continue;
819467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
820ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            // Skip text.
8216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
8226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
8236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
824ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        const Source itemSource = mSource.withLine(parser->getLineNumber());
8251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
8261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementName = parser->getElementName();
827ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        if (elementNamespace.empty() && (elementName == u"flag" || elementName == u"enum")) {
8281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (elementName == u"enum") {
8291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                if (typeMask & android::ResTable_map::TYPE_FLAGS) {
830ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                    mDiag->error(DiagMessage(itemSource)
8311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                 << "can not define an <enum>; already defined a <flag>");
8321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    error = true;
8331ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    continue;
8341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                }
8351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                typeMask |= android::ResTable_map::TYPE_ENUM;
836ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
8371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (elementName == u"flag") {
8381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                if (typeMask & android::ResTable_map::TYPE_ENUM) {
839ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                    mDiag->error(DiagMessage(itemSource)
8401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                 << "can not define a <flag>; already defined an <enum>");
8411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    error = true;
8421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    continue;
8431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                }
8441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                typeMask |= android::ResTable_map::TYPE_FLAGS;
8456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
8466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
8471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
848abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                Attribute::Symbol& symbol = s.value();
8499ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                ParsedResource childResource;
850abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                childResource.name = symbol.symbol.name.value();
851ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                childResource.source = itemSource;
8529ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                childResource.value = util::make_unique<Id>();
8539ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                outResource->childResources.push_back(std::move(childResource));
854ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
855abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                symbol.symbol.setComment(std::move(comment));
856abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                symbol.symbol.setSource(itemSource);
857abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski
858abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                auto insertResult = items.insert(std::move(symbol));
859abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                if (!insertResult.second) {
860abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                    const Attribute::Symbol& existingSymbol = *insertResult.first;
861abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                    mDiag->error(DiagMessage(itemSource)
862abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                                 << "duplicate symbol '" << existingSymbol.symbol.name.value().entry
863abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                                 << "'");
864abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski
865abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                    mDiag->note(DiagMessage(existingSymbol.symbol.getSource())
866abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                                << "first defined here");
867abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                    error = true;
868abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski                }
8696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            } else {
8701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
8716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
872ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
873ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
8746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            error = true;
8756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
876ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
877ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        comment = {};
8786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
8796f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
8806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (error) {
8819ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        return false;
8826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
8836f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
8846f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
885abf83cbe4f63cd76043aab89cd0e08525560fea2Adam Lesinski    attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
886ca2fc353c2b07e24e297fdc8426c7abd601d908bAdam Lesinski    attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
887a587065721053ad54e34f484868142407d59512dAdam Lesinski    if (maybeMin) {
888a587065721053ad54e34f484868142407d59512dAdam Lesinski        attr->minInt = maybeMin.value();
889a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
890a587065721053ad54e34f484868142407d59512dAdam Lesinski
891a587065721053ad54e34f484868142407d59512dAdam Lesinski    if (maybeMax) {
892a587065721053ad54e34f484868142407d59512dAdam Lesinski        attr->maxInt = maybeMax.value();
893a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
8949ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = std::move(attr);
8959ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
8966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
8976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
898467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam LesinskiMaybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
8991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                                             const StringPiece16& tag) {
9001ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const Source source = mSource.withLine(parser->getLineNumber());
9011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
902467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
9031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!maybeName) {
9041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
9051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return {};
9066f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
908467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeValue = xml::findNonEmptyAttribute(parser, u"value");
9091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!maybeValue) {
9101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
9111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return {};
9126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
9146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    android::Res_value val;
9151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!android::ResTable::stringToInt(maybeValue.value().data(),
9161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                        maybeValue.value().size(), &val)) {
9171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "invalid value '" << maybeValue.value()
9181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                     << "' for <" << tag << ">; must be an integer");
9191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return {};
9206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
9221ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    return Attribute::Symbol{
92352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
9246f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
9256f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
926467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskistatic Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
9276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    str = util::trimWhitespace(str);
928467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    const char16_t* start = str.data();
9296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const char16_t* const end = start + str.size();
9306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    const char16_t* p = start;
9316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
932467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Reference ref;
933467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    if (p != end && *p == u'*') {
934467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        ref.privateReference = true;
935467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        start++;
936467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        p++;
937467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
938467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
9396f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    StringPiece16 package;
9406f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    StringPiece16 name;
9416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    while (p != end) {
9426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        if (*p == u':') {
9436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            package = StringPiece16(start, p - start);
9446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            name = StringPiece16(p + 1, end - (p + 1));
9456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            break;
9466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
9476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        p++;
9486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
950467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    ref.name = ResourceName(package.toString(), ResourceType::kAttr,
951ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                        name.empty() ? str.toString() : name.toString());
952467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    return Maybe<Reference>(std::move(ref));
9536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
9546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
955467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) {
9561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const Source source = mSource.withLine(parser->getLineNumber());
9571ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
958467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
9591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!maybeName) {
9601ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
9616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
9626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
964467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<Reference> maybeKey = parseXmlAttributeName(maybeName.value());
9651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!maybeKey) {
9661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
9676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
9686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
970467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    transformReferenceFromNamespace(parser, u"", &maybeKey.value());
97128cacf091ad2b1c2749e77f590e9523e58735252Adam Lesinski    maybeKey.value().setSource(source);
9726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
9736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
9746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (!value) {
9751ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        mDiag->error(DiagMessage(source) << "could not parse style item");
9766f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
9776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
9786f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
979467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
9806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    return true;
9816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
9826f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
983467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
9847ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->name.type = ResourceType::kStyle;
9857ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
986bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski    std::unique_ptr<Style> style = util::make_unique<Style>();
9876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
988467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent");
9891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (maybeParent) {
9901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        // If the parent is empty, we don't have a parent, but we also don't infer either.
9911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!maybeParent.value().empty()) {
9921ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            std::string errStr;
9931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            style->parent = ResourceUtils::parseStyleParentReference(maybeParent.value(), &errStr);
9941ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (!style->parent) {
9957ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                mDiag->error(DiagMessage(outResource->source) << errStr);
9961ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                return false;
9971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
9986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
999467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            // Transform the namespace prefix to the actual package name, and mark the reference as
1000467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            // private if appropriate.
1001467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            transformReferenceFromNamespace(parser, u"", &style->parent.value());
10026f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
10031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1004bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski    } else {
1005bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski        // No parent was specified, so try inferring it from the style name.
10069ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski        std::u16string styleName = outResource->name.entry;
1007bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski        size_t pos = styleName.find_last_of(u'.');
1008bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski        if (pos != std::string::npos) {
1009bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski            style->parentInferred = true;
1010467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            style->parent = Reference(ResourceName({}, ResourceType::kStyle,
1011467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                                                   styleName.substr(0, pos)));
1012bdaa092a193d8ddccbd9ad8434be97878e6ded59Adam Lesinski        }
10136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
10146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
10161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
1017467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
1018467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
10191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            // Skip text and comments.
10206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
10216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
10226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
10241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementName = parser->getElementName();
10251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (elementNamespace == u"" && elementName == u"item") {
10261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            error |= !parseStyleItem(parser, style.get());
10271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1028ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
10291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
10301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << ":" << elementName << ">");
10311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            error = true;
10326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
10336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
10346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (error) {
10366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
10376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
10389ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
10399ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = std::move(style);
10409ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
10416f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
10426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10437ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
10447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_ANY);
10457ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
10467ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
10477ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
10487ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_INTEGER);
10497ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
10507ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
10517ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
10527ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_STRING);
10537ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
10547ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
10557ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskibool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
10567ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                    const uint32_t typeMask) {
10577ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->name.type = ResourceType::kArray;
10587ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
10596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::unique_ptr<Array> array = util::make_unique<Array>();
10606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    bool error = false;
10621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
1063467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
1064467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
10651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            // Skip text and comments.
10666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
10676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
10686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const Source itemSource = mSource.withLine(parser->getLineNumber());
10701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
10711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementName = parser->getElementName();
10721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (elementNamespace.empty() && elementName == u"item") {
10731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
10741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (!item) {
10751ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "could not parse array item");
10761ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
10771ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
10781ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
1079ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            item->setSource(itemSource);
10801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            array->items.emplace_back(std::move(item));
10816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1082ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
10831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
10841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << "unknown tag <" << elementNamespace << ":" << elementName << ">");
10856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            error = true;
10866f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
10876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
10886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
10896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    if (error) {
10906f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
10916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
10929ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
10939ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = std::move(array);
10949ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
10956f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
10966f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1097467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinskibool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
10987ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->name.type = ResourceType::kPlurals;
10997ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
11006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    std::unique_ptr<Plural> plural = util::make_unique<Plural>();
11016f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
11021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
11031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
1104467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
1105467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
11061ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            // Skip text and comments.
11076f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
11086f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
11096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1110ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        const Source itemSource = mSource.withLine(parser->getLineNumber());
11111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
11121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementName = parser->getElementName();
11131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (elementNamespace.empty() && elementName == u"item") {
1114467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            Maybe<StringPiece16> maybeQuantity = xml::findNonEmptyAttribute(parser, u"quantity");
1115467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            if (!maybeQuantity) {
1116ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
11171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "'quantity'");
11181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
11191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
11201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
11216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1122467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            StringPiece16 trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
11231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            size_t index = 0;
11241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (trimmedQuantity == u"zero") {
11251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::Zero;
11261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (trimmedQuantity == u"one") {
11271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::One;
11281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (trimmedQuantity == u"two") {
11291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::Two;
11301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (trimmedQuantity == u"few") {
11311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::Few;
11321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (trimmedQuantity == u"many") {
11331ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::Many;
11341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else if (trimmedQuantity == u"other") {
11351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                index = Plural::Other;
11361ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else {
1137ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                mDiag->error(DiagMessage(itemSource)
11381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "<item> in <plural> has invalid value '" << trimmedQuantity
11391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "' for attribute 'quantity'");
11401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
11411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
11421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
11436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
11441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (plural->values[index]) {
1145ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                mDiag->error(DiagMessage(itemSource)
11461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                             << "duplicate quantity '" << trimmedQuantity << "'");
11471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
11481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
11491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
11506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
11511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (!(plural->values[index] = parseXml(parser, android::ResTable_map::TYPE_STRING,
11521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                                   kNoRawString))) {
11531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
11541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
1155ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            plural->values[index]->setSource(itemSource);
1156ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
1157ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
1158ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
11591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << elementName << ">");
11601ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            error = true;
11616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
11626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
11636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
11641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (error) {
11656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
11666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
11679ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
11689ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = std::move(plural);
11699ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
11706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
11716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
117252364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinskibool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
117352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski                                           ParsedResource* outResource) {
11747ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    outResource->name.type = ResourceType::kStyleable;
11756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1176b274e35abfbbd09e0fce983a215c11522c56cce2Adam Lesinski    // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
11779f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski    outResource->symbolState = SymbolState::kPublic;
11789f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
117952364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    // Declare-styleable only ends up in default config;
118052364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    if (outResource->config != ConfigDescription::defaultConfig()) {
118152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
118252364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski                            << outResource->config << "' for styleable "
118352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski                            << outResource->name.entry);
118452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        outResource->config = ConfigDescription::defaultConfig();
118552364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    }
118652364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
11877ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
11887ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
11891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::u16string comment;
11901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
11911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    const size_t depth = parser->getDepth();
1192467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    while (xml::XmlPullParser::nextChildNode(parser, depth)) {
1193467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
1194ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            comment = util::trimWhitespace(parser->getComment()).toString();
1195ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            continue;
1196467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
1197ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            // Ignore text.
11986f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            continue;
11996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
12006f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1201ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        const Source itemSource = mSource.withLine(parser->getLineNumber());
12021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementNamespace = parser->getElementNamespace();
12031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        const std::u16string& elementName = parser->getElementName();
12041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (elementNamespace.empty() && elementName == u"attr") {
1205467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
1206467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            if (!maybeName) {
1207ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
12081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
12096f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                continue;
12106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
12116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1212467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            // If this is a declaration, the package name may be in the name. Separate these out.
1213467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            // Eg. <attr name="android:text" />
1214467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            Maybe<Reference> maybeRef = parseXmlAttributeName(maybeName.value());
1215467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            if (!maybeRef) {
1216467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
1217467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                             << maybeName.value() << "'");
1218467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                error = true;
1219467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski                continue;
1220467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            }
1221467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
1222467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            Reference& childRef = maybeRef.value();
1223467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            xml::transformReferenceFromNamespace(parser, u"", &childRef);
1224467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
1225ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            // Create the ParsedResource that will add the attribute to the table.
12269ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            ParsedResource childResource;
1227467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski            childResource.name = childRef.name.value();
1228ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            childResource.source = itemSource;
1229ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            childResource.comment = std::move(comment);
12306f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
12319ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski            if (!parseAttrImpl(parser, &childResource, true)) {
12321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
12336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski                continue;
12346f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski            }
12356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1236ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            // Create the reference to this attribute.
1237ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            childRef.setComment(childResource.comment);
1238ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            childRef.setSource(itemSource);
1239ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            styleable->entries.push_back(std::move(childRef));
12401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1241ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            outResource->childResources.push_back(std::move(childResource));
12426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1243ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
1244ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski            mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
12451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                         << elementName << ">");
12461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            error = true;
12476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
1248ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
1249ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski        comment = {};
12506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
12516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
12521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (error) {
12536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
12546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
12559ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
12569ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    outResource->value = std::move(styleable);
12579ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    return true;
12586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
12596f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
12606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski} // namespace aapt
1261