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"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <functional>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <sstream>
21ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "android-base/logging.h"
23ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceTable.h"
251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceUtils.h"
266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceValues.h"
271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ValueVisitor.h"
287ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski#include "util/ImmutableMap.h"
297542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski#include "util/Maybe.h"
309e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski#include "util/Util.h"
31467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
329e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
33d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinskiusing android::StringPiece;
34d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
356f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
37d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskiconstexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
39d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski// Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
40d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskistatic bool ShouldIgnoreElement(const StringPiece& ns, const StringPiece& name) {
41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return ns.empty() && (name == "skip" || name == "eat-comment");
4227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
4327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
44d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskistatic uint32_t ParseFormatTypeNoEnumsOrFlags(const StringPiece& piece) {
45d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  if (piece == "reference") {
46cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_REFERENCE;
47d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "string") {
48cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_STRING;
49d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "integer") {
50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_INTEGER;
51d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "boolean") {
52cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_BOOLEAN;
53d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "color") {
54cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_COLOR;
55d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "float") {
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_FLOAT;
57d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "dimension") {
58cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_DIMENSION;
59d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "fraction") {
60cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_FRACTION;
61d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  }
62d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  return 0;
63d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski}
64d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski
65d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskistatic uint32_t ParseFormatType(const StringPiece& piece) {
66d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  if (piece == "enum") {
67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_ENUM;
68d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  } else if (piece == "flags") {
69cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return android::ResTable_map::TYPE_FLAGS;
70d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  }
71d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  return ParseFormatTypeNoEnumsOrFlags(piece);
727ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
737ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
74ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic uint32_t ParseFormatAttribute(const StringPiece& str) {
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  uint32_t mask = 0;
76ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (StringPiece part : util::Tokenize(str, '|')) {
77ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    StringPiece trimmed_part = util::TrimWhitespace(part);
78ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    uint32_t type = ParseFormatType(trimmed_part);
79cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (type == 0) {
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 0;
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    mask |= type;
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return mask;
857ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
867ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
87d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski// A parsed resource ready to be added to the ResourceTable.
887ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinskistruct ParsedResource {
89cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceName name;
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ConfigDescription config;
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string product;
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Source source;
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceId id;
94ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<SymbolState> symbol_state;
954488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski  bool allow_new = false;
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string comment;
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Value> value;
98ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::list<ParsedResource> child_resources;
997ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski};
1007ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1017ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski// Recursively adds resources to the ResourceTable.
1024488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinskistatic bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_comment.size() != res->comment.size()) {
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Only if there was a change do we re-assign.
106d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski    res->comment = trimmed_comment.to_string();
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (res->symbol_state) {
110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    Symbol symbol;
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    symbol.state = res->symbol_state.value();
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    symbol.source = res->source;
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    symbol.comment = res->comment;
1144488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski    symbol.allow_new = res->allow_new;
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!table->SetSymbolState(res->name, res->id, symbol, diag)) {
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (res->value) {
121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Attach the comment, source and config to the value.
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    res->value->SetComment(std::move(res->comment));
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    res->value->SetSource(std::move(res->source));
124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1254488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski    if (!table->AddResource(res->name, res->id, res->config, res->product, std::move(res->value),
1264488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski                            diag)) {
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
132ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (ParsedResource& child : res->child_resources) {
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    error |= !AddResourcesToTable(table, diag, &child);
134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return !error;
1367ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
1377ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1387ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski// Convenient aliases for more readable function calls.
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskienum { kAllowRawString = true, kNoRawString = false };
1407ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table,
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                               const Source& source,
1439ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski                               const ConfigDescription& config,
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                               const ResourceParserOptions& options)
145ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    : diag_(diag),
146ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      table_(table),
147ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      source_(source),
148ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      config_(config),
149ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      options_(options) {}
1506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1516f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/**
1526f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Build a string from XML that converts nested elements into Span objects.
1536f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
1547542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinskibool ResourceParser::FlattenXmlSubtree(
1557542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
1567542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    std::vector<UntranslatableSection>* out_untranslatable_sections) {
1577542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
1588049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  // The stack elements refer to the indices in out_style_string->spans.
1598049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  // By first adding to the out_style_string->spans vector, and then using the stack to refer
1608049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
1618049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski  std::vector<size_t> span_stack;
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1637542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // Clear the output variables.
164ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_raw_string->clear();
165ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_style_string->spans.clear();
1667542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  out_untranslatable_sections->clear();
1677542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1687542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // The StringBuilder will concatenate the various segments of text which are initially
1697542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // separated by tags. It also handles unicode escape codes and quotations.
170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  util::StringBuilder builder;
1717542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
1727542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
1737542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  Maybe<size_t> untranslatable_start_depth;
1747542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski
175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  size_t depth = 1;
176ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const xml::XmlPullParser::Event event = parser->event();
178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1797542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    if (event == xml::XmlPullParser::Event::kStartElement) {
1807542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      if (parser->element_namespace().empty()) {
1817542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // This is an HTML tag which we encode as a span. Add it to the span stack.
1827542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        std::string span_name = parser->element_name();
1837542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        const auto end_attr_iter = parser->end_attributes();
1847542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter; ++attr_iter) {
1857542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          span_name += ";";
1867542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          span_name += attr_iter->name;
1877542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          span_name += "=";
1887542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          span_name += attr_iter->value;
1897542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        }
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1917542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // Make sure the string is representable in our binary format.
1927542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        if (builder.Utf16Len() > std::numeric_limits<uint32_t>::max()) {
1937542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1947542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski                       << "style string '" << builder.ToString() << "' is too long");
1957542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          return false;
1967542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        }
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1988049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski        out_style_string->spans.push_back(
1998049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski            Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
2008049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski        span_stack.push_back(out_style_string->spans.size() - 1);
2017542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      } else if (parser->element_namespace() == sXliffNamespaceUri) {
2027542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        if (parser->element_name() == "g") {
2037542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          if (untranslatable_start_depth) {
2047542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            // We've already encountered an <xliff:g> tag, and nested <xliff:g> tags are illegal.
2057542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
2067542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski                         << "illegal nested XLIFF 'g' tag");
2077542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            return false;
2087542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          } else {
2097542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            // Mark the start of an untranslatable section. Use UTF8 indices/lengths.
2107542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            untranslatable_start_depth = depth;
2117542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            const size_t current_idx = builder.ToString().size();
2127542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            out_untranslatable_sections->push_back(UntranslatableSection{current_idx, current_idx});
2137542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski          }
2146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        }
2157542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // Ignore other xliff tags, they get handled by other tools.
216cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
2177542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      } else {
2187542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // Besides XLIFF, any other namespaced tag is unsupported and ignored.
2197542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
2207542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski                    << "ignoring element '" << parser->element_name()
2217542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski                    << "' with unknown namespace '" << parser->element_namespace() << "'");
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
2247542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      // Enter one level inside the element.
2257542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      depth++;
2267542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    } else if (event == xml::XmlPullParser::Event::kText) {
2277542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      // Record both the raw text and append to the builder to deal with escape sequences
2287542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      // and quotations.
2297542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      out_raw_string->append(parser->text());
2307542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      builder.Append(parser->text());
2317542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    } else if (event == xml::XmlPullParser::Event::kEndElement) {
2327542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      // Return one level from within the element.
2337542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      depth--;
2347542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      if (depth == 0) {
2357542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        break;
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
2387542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      if (parser->element_namespace().empty()) {
2397542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // This is an HTML tag which we encode as a span. Update the span
2407542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // stack and pop the top entry.
2418049f3da712ea9c3154b57ce2276c97e749d1f2cAdam Lesinski        Span& top_span = out_style_string->spans[span_stack.back()];
2427542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        top_span.last_char = builder.Utf16Len() - 1;
2437542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        span_stack.pop_back();
2447542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      } else if (untranslatable_start_depth == make_value(depth)) {
2457542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        // This is the end of an untranslatable section. Use UTF8 indices/lengths.
2467542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        UntranslatableSection& untranslatable_section = out_untranslatable_sections->back();
2477542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        untranslatable_section.end = builder.ToString().size();
2487542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        untranslatable_start_depth = {};
2497542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      }
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else if (event == xml::XmlPullParser::Event::kComment) {
2517542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski      // Ignore.
252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
253ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      LOG(FATAL) << "unhandled XML event";
2546f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
2566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
2577542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  CHECK(span_stack.empty()) << "spans haven't been fully processed";
258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_style_string->str = builder.ToString();
2597542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  return true;
2606f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2616f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
262ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::Parse(xml::XmlPullParser* parser) {
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
264ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
265ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
266ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip comments and text.
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
2706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
271ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!parser->element_namespace().empty() ||
272ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        parser->element_name() != "resources") {
273ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "root element must be <resources>");
275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
2776f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
278ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    error |= !ParseResources(parser);
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    break;
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  };
2816f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
282ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (parser->event() == xml::XmlPullParser::Event::kBadDocument) {
283ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
284ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                 << "xml parser error: " << parser->error());
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return !error;
2886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
2896f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
291ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::set<ResourceName> stripped_resources;
2926f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string comment;
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const xml::XmlPullParser::Event event = parser->event();
298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (event == xml::XmlPullParser::Event::kComment) {
299ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      comment = parser->comment();
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
3021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (event == xml::XmlPullParser::Event::kText) {
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!util::TrimWhitespace(parser->text()).empty()) {
305ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "plain text not allowed here");
307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
31127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
312ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CHECK(event == xml::XmlPullParser::Event::kStartElement);
31327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
314ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!parser->element_namespace().empty()) {
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip unknown namespace.
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
3176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
3189ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
319ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string element_name = parser->element_name();
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_name == "skip" || element_name == "eat-comment") {
321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      comment = "";
322cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
3239ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    }
3249ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ParsedResource parsed_resource;
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parsed_resource.config = config_;
327ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parsed_resource.source = source_.WithLine(parser->line_number());
328ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parsed_resource.comment = std::move(comment);
3296f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Extract the product name if it exists.
331ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (Maybe<StringPiece> maybe_product =
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            xml::FindNonEmptyAttribute(parser, "product")) {
333d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      parsed_resource.product = maybe_product.value().to_string();
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
3357ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Parse the resource regardless of product.
337ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!ParseResource(parser, &parsed_resource)) {
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
3417ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
342ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!AddResourcesToTable(table_, diag_, &parsed_resource)) {
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
3447ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
3467ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
347cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Check that we included at least one variant of each stripped resource.
348ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (const ResourceName& stripped_resource : stripped_resources) {
349ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!table_->FindResource(stripped_resource)) {
350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Failed to find the resource.
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_)
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "resource '" << stripped_resource
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "' "
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                      "was filtered out but no product variant remains");
355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
3587ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return !error;
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
3617ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
362ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseResource(xml::XmlPullParser* parser,
363ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                   ParsedResource* out_resource) {
364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  struct ItemTypeFormat {
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    ResourceType type;
366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    uint32_t format;
367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  };
368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
369cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
370cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                          ParsedResource*)>;
371cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
37286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({
37386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}},
37486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}},
37586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"configVarying", {ResourceType::kConfigVarying, android::ResTable_map::TYPE_ANY}},
37686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"dimen",
37786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski       {ResourceType::kDimen,
37886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
37986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski            android::ResTable_map::TYPE_DIMENSION}},
38086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"drawable", {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}},
38186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"fraction",
38286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski       {ResourceType::kFraction,
38386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
38486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski            android::ResTable_map::TYPE_DIMENSION}},
38586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"integer", {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
38686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"string", {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
38786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  });
38886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
38986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({
39086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)},
39186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"array", std::mem_fn(&ResourceParser::ParseArray)},
39286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"attr", std::mem_fn(&ResourceParser::ParseAttr)},
39386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"configVarying",
39486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski       std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kConfigVarying,
39586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                 std::placeholders::_2, std::placeholders::_3)},
39686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
39786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
39886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
39986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
40086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"public", std::mem_fn(&ResourceParser::ParsePublic)},
40186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
40286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
40386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
40486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                          std::placeholders::_2, std::placeholders::_3)},
40586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
40686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  });
407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
408ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string resource_type = parser->element_name();
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The value format accepted for this resource.
411ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  uint32_t resource_format = 0u;
412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
41386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  bool can_be_item = true;
41486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  bool can_be_bag = true;
415ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (resource_type == "item") {
41686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    can_be_bag = false;
41786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
418e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski    // The default format for <item> is any. If a format attribute is present, that one will
419e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski    // override the default.
420e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski    resource_format = android::ResTable_map::TYPE_ANY;
421e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Items have their type encoded in the type attribute.
423d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski    if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
424d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      resource_type = maybe_type.value().to_string();
425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
426ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "<item> must have a 'type' attribute");
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
431d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski    if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // An explicit format for this resource was specified. The resource will
433d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      // retain its type in its name, but the accepted value for this type is
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // overridden.
435d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      resource_format = ParseFormatTypeNoEnumsOrFlags(maybe_format.value());
436ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!resource_format) {
437ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(out_resource->source)
438ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                     << "'" << maybe_format.value()
439ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                     << "' is an invalid format");
440cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        return false;
441cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
4427ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
44386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  } else if (resource_type == "bag") {
44486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    can_be_item = false;
44586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
44686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    // Bags have their type encoded in the type attribute.
44786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
44886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      resource_type = maybe_type.value().to_string();
44986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    } else {
45086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
45186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                   << "<bag> must have a 'type' attribute");
45286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      return false;
45386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    }
454cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4557ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
456cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Get the name of the resource. This will be checked later, because not all
457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // XML elements require a name.
458ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
4597ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
460ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (resource_type == "id") {
461ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_name) {
462ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(out_resource->source)
463ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "<" << parser->element_name()
464cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "> missing 'name' attribute");
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
466cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
4677ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
468ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->name.type = ResourceType::kId;
469d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski    out_resource->name.entry = maybe_name.value().to_string();
470ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->value = util::make_unique<Id>();
471cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
472cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4737ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
47486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  if (can_be_item) {
47586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    const auto item_iter = elToItemMap.find(resource_type);
47686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    if (item_iter != elToItemMap.end()) {
47786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      // This is an item, record its type and format and start parsing.
4787ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
47986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      if (!maybe_name) {
48086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        diag_->Error(DiagMessage(out_resource->source)
48186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                     << "<" << parser->element_name() << "> missing 'name' attribute");
48286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        return false;
48386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
4847ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
48586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      out_resource->name.type = item_iter->second.type;
48686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      out_resource->name.entry = maybe_name.value().to_string();
4877ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
488e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski      // Only use the implied format of the type when there is no explicit format.
489e597d68d33c76c2b830f5497ed4ba74c5193a056Adam Lesinski      if (resource_format == 0u) {
49086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        resource_format = item_iter->second.format;
49186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
4927ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
49386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      if (!ParseItem(parser, out_resource, resource_format)) {
49486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        return false;
49586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
49686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      return true;
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // This might be a bag or something.
50186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  if (can_be_bag) {
50286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    const auto bag_iter = elToBagMap.find(resource_type);
50386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    if (bag_iter != elToBagMap.end()) {
50486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      // Ensure we have a name (unless this is a <public-group>).
50586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      if (resource_type != "public-group") {
50686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        if (!maybe_name) {
50786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski          diag_->Error(DiagMessage(out_resource->source)
50886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                       << "<" << parser->element_name() << "> missing 'name' attribute");
50986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski          return false;
51086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        }
51186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
51286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        out_resource->name.entry = maybe_name.value().to_string();
51386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
51486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
51586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      // Call the associated parse method. The type will be filled in by the
51686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      // parse func.
51786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      if (!bag_iter->second(this, parser, out_resource)) {
51886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        return false;
51986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
52086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      return true;
52186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    }
52286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  }
52386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski
52486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  if (can_be_item) {
52586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    // Try parsing the elementName (or type) as a resource. These shall only be
52686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    // resources like 'layout' or 'xml' and they can only be references.
52786d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    const ResourceType* parsed_type = ParseResourceType(resource_type);
52886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski    if (parsed_type) {
529ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!maybe_name) {
530ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(out_resource->source)
531ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                     << "<" << parser->element_name()
532cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "> missing 'name' attribute");
533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        return false;
534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
5357ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
53686d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      out_resource->name.type = *parsed_type;
537d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      out_resource->name.entry = maybe_name.value().to_string();
53886d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
53986d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      if (!out_resource->value) {
54086d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        diag_->Error(DiagMessage(out_resource->source)
54186d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski                     << "invalid value for type '" << *parsed_type << "'. Expected a reference");
54286d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski        return false;
54386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      }
54486d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski      return true;
5457ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski    }
546cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
547cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
548ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  diag_->Warn(DiagMessage(out_resource->source)
549ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              << "unknown resource type '" << parser->element_name() << "'");
550cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
552cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
553ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseItem(xml::XmlPullParser* parser,
554ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                               ParsedResource* out_resource,
555cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                               const uint32_t format) {
556cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (format == android::ResTable_map::TYPE_STRING) {
557ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return ParseString(parser, out_resource);
558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
560ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = ParseXml(parser, format, kNoRawString);
561ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!out_resource->value) {
562ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source) << "invalid "
563ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << out_resource->name.type);
564cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
565cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
5677ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
5686f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
5696f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/**
5706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Reads the entire XML subtree and attempts to parse it as some Item,
5716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * with typeMask denoting which items it can be. If allowRawValue is
5726f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * true, a RawString is returned if the XML couldn't be parsed as
5736f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * an Item. If allowRawValue is false, nullptr is returned in this
5746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * case.
5756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
576ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
577ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                               const uint32_t type_mask,
578ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                               const bool allow_raw_value) {
579ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t begin_xml_line = parser->line_number();
580ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
581ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string raw_value;
582ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StyleString style_string;
5837542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  std::vector<UntranslatableSection> untranslatable_sections;
5847542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
5856f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    return {};
586cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
587cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
588ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!style_string.spans.empty()) {
589cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // This can only be a StyledString.
5907542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    std::unique_ptr<StyledString> styled_string =
5917542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        util::make_unique<StyledString>(table_->string_pool.MakeRef(
5927542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski            style_string, StringPool::Context(StringPool::Context::kStylePriority, config_)));
5937542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    styled_string->untranslatable_sections = std::move(untranslatable_sections);
5947542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    return std::move(styled_string);
595cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
596cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
597ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  auto on_create_reference = [&](const ResourceName& name) {
598cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // name.package can be empty here, as it will assume the package name of the
599cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // table.
600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::unique_ptr<Id> id = util::make_unique<Id>();
601ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    id->SetSource(source_.WithLine(begin_xml_line));
602ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    table_->AddResource(name, {}, {}, std::move(id), diag_);
603cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  };
604cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
605cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Process the raw value.
606ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<Item> processed_item =
607ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ResourceUtils::TryParseItemForAttribute(raw_value, type_mask,
608ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                              on_create_reference);
609ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (processed_item) {
610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Fix up the reference.
611ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
612ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      TransformReferenceFromNamespace(parser, "", ref);
613cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
614ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return processed_item;
615cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
616cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
617cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Try making a regular string.
618ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_STRING) {
619cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Use the trimmed, escaped string.
6207542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    std::unique_ptr<String> string = util::make_unique<String>(
6217542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski        table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
6227542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    string->untranslatable_sections = std::move(untranslatable_sections);
6237542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    return std::move(string);
624cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
625cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
62690919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
62790919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  if (util::TrimWhitespace(raw_value).empty()) {
62890919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski    return ResourceUtils::MakeNull();
62990919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski  }
63090919978e7ccf7dc25622e9d039a7e87ebe7ba11Adam Lesinski
631ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (allow_raw_value) {
632cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // We can't parse this so return a RawString if we are allowed.
633cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return util::make_unique<RawString>(
634ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        table_->string_pool.MakeRef(raw_value, StringPool::Context(config_)));
635cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
6376f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
639ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseString(xml::XmlPullParser* parser,
640ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                 ParsedResource* out_resource) {
641cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool formatted = true;
642ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (Maybe<StringPiece> formatted_attr =
643ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          xml::FindAttribute(parser, "formatted")) {
644ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    Maybe<bool> maybe_formatted =
645ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        ResourceUtils::ParseBool(formatted_attr.value());
646ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_formatted) {
647ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(out_resource->source)
648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "invalid value for 'formatted'. Must be a boolean");
649cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
651ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    formatted = maybe_formatted.value();
652cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
653cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
6547542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  bool translatable = options_.translatable;
6557542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
6567542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
6577542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    if (!maybe_translatable) {
658ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(out_resource->source)
659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "invalid value for 'translatable'. Must be a boolean");
660cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
6627542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    translatable = maybe_translatable.value();
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
664cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
665ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value =
666ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
667ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!out_resource->value) {
668ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source) << "not a valid string");
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
670cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
671cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
672ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (String* string_value = ValueCast<String>(out_resource->value.get())) {
6737542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    string_value->SetTranslatable(translatable);
674cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
6757542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    if (formatted && translatable) {
676ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!util::VerifyJavaStringFormat(*string_value->value)) {
677ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        DiagMessage msg(out_resource->source);
678cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "multiple substitutions specified in non-positional format; "
679cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               "did you mean to add the formatted=\"false\" attribute?";
680ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (options_.error_on_positional_arguments) {
681ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          diag_->Error(msg);
682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          return false;
683b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski        }
6841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
685ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Warn(msg);
686cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
6876f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
688b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski
6897542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  } else if (StyledString* string_value = ValueCast<StyledString>(out_resource->value.get())) {
6907542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    string_value->SetTranslatable(translatable);
691cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
6936f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6946f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
695ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParsePublic(xml::XmlPullParser* parser,
696ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                 ParsedResource* out_resource) {
697ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
698ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_type) {
699ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source)
700cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 << "<public> must have a 'type' attribute");
701cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
702cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7039e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
704ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
705ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!parsed_type) {
706ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
707ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << maybe_type.value()
708ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << "' in <public>");
709cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
710cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
71127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
712ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = *parsed_type;
71327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
714ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
715ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
716ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_id) {
717ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      diag_->Error(DiagMessage(out_resource->source)
718ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                   << "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
719cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
72027afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski    }
721ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->id = maybe_id.value();
722cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
72327afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
724ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (*parsed_type == ResourceType::kId) {
725cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // An ID marked as public is also the definition of an ID.
726ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->value = util::make_unique<Id>();
727cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
72827afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
729ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->symbol_state = SymbolState::kPublic;
730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
73127afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski}
73227afb9e8894b512b21fcca6ce142f40f1ee16cbbAdam Lesinski
733ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser,
734ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                      ParsedResource* out_resource) {
735ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
736ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_type) {
737ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source)
738cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 << "<public-group> must have a 'type' attribute");
739cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
740cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7419e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
742ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
743ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!parsed_type) {
744ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
745ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << maybe_type.value()
746ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << "' in <public-group>");
747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
748cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
750ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_id_str =
751ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      xml::FindNonEmptyAttribute(parser, "first-id");
752ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_id_str) {
753ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source)
754cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 << "<public-group> must have a 'first-id' attribute");
755a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    return false;
756cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
757cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
758ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<ResourceId> maybe_id =
759ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ResourceUtils::ParseResourceId(maybe_id_str.value());
760ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_id) {
761ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
762ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << maybe_id_str.value()
763ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                   << "' in <public-group>");
764cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
765cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
766cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
767ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceId next_id = maybe_id.value();
768cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
769cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string comment;
770cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
771ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
772ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
773ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() == xml::XmlPullParser::Event::kComment) {
774d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      comment = util::TrimWhitespace(parser->comment()).to_string();
775cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
776ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
777cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip text.
778cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
779cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
780cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
781ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const Source item_source = source_.WithLine(parser->line_number());
782ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
783ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
784ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace.empty() && element_name == "public") {
785ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Maybe<StringPiece> maybe_name =
786ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          xml::FindNonEmptyAttribute(parser, "name");
787ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!maybe_name) {
788ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
789cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "<public> must have a 'name' attribute");
790cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
791cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
792cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
793cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
794ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (xml::FindNonEmptyAttribute(parser, "id")) {
795ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
796cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "'id' is ignored within <public-group>");
797cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
798cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
799cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
800cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
801ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (xml::FindNonEmptyAttribute(parser, "type")) {
802ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
803cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "'type' is ignored within <public-group>");
804cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
805cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
806cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
807cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
808ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ParsedResource child_resource;
809ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.name.type = *parsed_type;
810d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      child_resource.name.entry = maybe_name.value().to_string();
811ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.id = next_id;
812ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.comment = std::move(comment);
813ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.source = item_source;
814ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.symbol_state = SymbolState::kPublic;
815ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_resource->child_resources.push_back(std::move(child_resource));
816cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
817ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      next_id.id += 1;
818cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
819ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
820ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
821cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
822cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
823cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
824cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return !error;
825a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
826a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
827ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
828ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                     ParsedResource* out_resource) {
829ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
830ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_type) {
831ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source)
832ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                 << "<" << parser->element_name()
833cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 << "> must have a 'type' attribute");
834a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski    return false;
835cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
836a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
837ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
838ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!parsed_type) {
839ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(out_resource->source)
840ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                 << "invalid resource type '" << maybe_type.value() << "' in <"
841ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                 << parser->element_name() << ">");
842cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
843cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
8446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
845ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = *parsed_type;
846cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
8476f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
8486f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
849ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseSymbol(xml::XmlPullParser* parser,
850ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                 ParsedResource* out_resource) {
851ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (ParseSymbolImpl(parser, out_resource)) {
852ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->symbol_state = SymbolState::kPrivate;
853cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
854cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
855cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
856cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
8576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
858ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseAddResource(xml::XmlPullParser* parser,
859ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                      ParsedResource* out_resource) {
860ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (ParseSymbolImpl(parser, out_resource)) {
861ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->symbol_state = SymbolState::kUndefined;
8624488f1c74a0f7df09f2b201f7caa228d729e8389Adam Lesinski    out_resource->allow_new = true;
863cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
864cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
865cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
866cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
8676f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
868ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
869ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                               ParsedResource* out_resource) {
870ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return ParseAttrImpl(parser, out_resource, false);
871cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
872a587065721053ad54e34f484868142407d59512dAdam Lesinski
873ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
874ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                   ParsedResource* out_resource, bool weak) {
875ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = ResourceType::kAttr;
876a587065721053ad54e34f484868142407d59512dAdam Lesinski
877cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Attributes only end up in default configuration.
878ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (out_resource->config != ConfigDescription::DefaultConfig()) {
879ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Warn(DiagMessage(out_resource->source)
880ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                << "ignoring configuration '" << out_resource->config
881ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                << "' for attribute " << out_resource->name);
882ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->config = ConfigDescription::DefaultConfig();
883cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
884cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
885ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  uint32_t type_mask = 0;
886cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
887ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
888ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (maybe_format) {
889ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    type_mask = ParseFormatAttribute(maybe_format.value());
890ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (type_mask == 0) {
891ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
892ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "invalid attribute format '" << maybe_format.value()
893cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "'");
894cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
895cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
896cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
897cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
898ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<int32_t> maybe_min, maybe_max;
899cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
900ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
901ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
902ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!min_str.empty()) {
903ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::u16string min_str16 = util::Utf8ToUtf16(min_str);
904cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      android::Res_value value;
905ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(),
906cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                         &value)) {
907ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        maybe_min = static_cast<int32_t>(value.data);
908cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
909cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
910cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
911ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_min) {
912ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
913ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "invalid 'min' value '" << min_str << "'");
914cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
915cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
916cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
917cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
918ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
919ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
920ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!max_str.empty()) {
921ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::u16string max_str16 = util::Utf8ToUtf16(max_str);
922cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      android::Res_value value;
923ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(),
924cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                         &value)) {
925ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        maybe_max = static_cast<int32_t>(value.data);
926cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
927cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
928cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
929ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_max) {
930ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
931ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "invalid 'max' value '" << max_str << "'");
932cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
933cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
934cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
935cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
936ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if ((maybe_min || maybe_max) &&
937ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) {
938ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
939cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 << "'min' and 'max' can only be used when format='integer'");
940cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
941cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
942cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
943cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  struct SymbolComparator {
944cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
945cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return a.symbol.name.value() < b.symbol.name.value();
946cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
947cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  };
948cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
949cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::set<Attribute::Symbol, SymbolComparator> items;
950cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
951cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string comment;
952cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
953ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
954ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
955ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() == xml::XmlPullParser::Event::kComment) {
956d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      comment = util::TrimWhitespace(parser->comment()).to_string();
957cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
958ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
959cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip text.
960cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
961cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
962cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
963ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const Source item_source = source_.WithLine(parser->line_number());
964ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
965ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
966ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace.empty() &&
967ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        (element_name == "flag" || element_name == "enum")) {
968ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (element_name == "enum") {
969ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (type_mask & android::ResTable_map::TYPE_FLAGS) {
970ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          diag_->Error(DiagMessage(item_source)
971cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                       << "can not define an <enum>; already defined a <flag>");
972cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          error = true;
973cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          continue;
974a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
975ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        type_mask |= android::ResTable_map::TYPE_ENUM;
976cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
977ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (element_name == "flag") {
978ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (type_mask & android::ResTable_map::TYPE_ENUM) {
979ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          diag_->Error(DiagMessage(item_source)
980cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                       << "can not define a <flag>; already defined an <enum>");
981cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          error = true;
982cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          continue;
983a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
984ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        type_mask |= android::ResTable_map::TYPE_FLAGS;
985cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
986cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
987cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (Maybe<Attribute::Symbol> s =
988ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              ParseEnumOrFlagItem(parser, element_name)) {
989cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        Attribute::Symbol& symbol = s.value();
990ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        ParsedResource child_resource;
991ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        child_resource.name = symbol.symbol.name.value();
992ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        child_resource.source = item_source;
993ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        child_resource.value = util::make_unique<Id>();
994ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        out_resource->child_resources.push_back(std::move(child_resource));
995ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
996ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        symbol.symbol.SetComment(std::move(comment));
997ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        symbol.symbol.SetSource(item_source);
998ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
999ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        auto insert_result = items.insert(std::move(symbol));
1000ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (!insert_result.second) {
1001ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          const Attribute::Symbol& existing_symbol = *insert_result.first;
1002ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          diag_->Error(DiagMessage(item_source)
1003cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                       << "duplicate symbol '"
1004ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       << existing_symbol.symbol.name.value().entry << "'");
1005cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1006ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          diag_->Note(DiagMessage(existing_symbol.symbol.GetSource())
1007cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                      << "first defined here");
1008cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          error = true;
1009a587065721053ad54e34f484868142407d59512dAdam Lesinski        }
1010cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
1011cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1012cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1013ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1014ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
1015cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
1016a587065721053ad54e34f484868142407d59512dAdam Lesinski    }
1017a587065721053ad54e34f484868142407d59512dAdam Lesinski
1018cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    comment = {};
1019cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
10206f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1021cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
1022cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1023cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1024cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1025cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
1026cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
1027ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  attr->type_mask =
1028ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      type_mask ? type_mask : uint32_t(android::ResTable_map::TYPE_ANY);
1029ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (maybe_min) {
1030ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    attr->min_int = maybe_min.value();
1031cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1032cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1033ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (maybe_max) {
1034ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    attr->max_int = maybe_max.value();
1035cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1036ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = std::move(attr);
1037cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
1038cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
1039ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
1040ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
1041cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    xml::XmlPullParser* parser, const StringPiece& tag) {
1042ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const Source source = source_.WithLine(parser->line_number());
10436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1044ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1045ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_name) {
1046ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
1047cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << tag << ">");
1048cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
1049cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
10506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1051ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
1052ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_value) {
1053ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
1054cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << tag << ">");
1055cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
1056cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1057cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1058ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::u16string value16 = util::Utf8ToUtf16(maybe_value.value());
1059cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value val;
1060cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
1061ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "invalid value '" << maybe_value.value()
1062cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << "' for <" << tag
1063cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << ">; must be an integer");
1064cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
1065cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1066a587065721053ad54e34f484868142407d59512dAdam Lesinski
1067cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return Attribute::Symbol{
1068ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
1069cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      val.data};
10706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
10716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1072ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
1073ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const Source source = source_.WithLine(parser->line_number());
10741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1075ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1076ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_name) {
1077ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
1078cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1079cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
10806f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1081ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<Reference> maybe_key =
1082ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ResourceUtils::ParseXmlAttributeName(maybe_name.value());
1083ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!maybe_key) {
1084ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "invalid attribute name '"
1085ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                     << maybe_name.value() << "'");
1086cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1087cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
10886f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1089ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  TransformReferenceFromNamespace(parser, "", &maybe_key.value());
1090ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  maybe_key.value().SetSource(source);
10916f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1092ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
1093cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!value) {
1094ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Error(DiagMessage(source) << "could not parse style item");
1095cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1096cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
10976f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1098cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  style->entries.push_back(
1099ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Style::Entry{std::move(maybe_key.value()), std::move(value)});
1100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
1101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
11021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
110386d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinskibool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
1104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                ParsedResource* out_resource) {
110586d67df8d57b9537666f9b54a9ca563779a2288bAdam Lesinski  out_resource->name.type = type;
1106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Style> style = util::make_unique<Style>();
1108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
1110ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (maybe_parent) {
1111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // If the parent is empty, we don't have a parent, but we also don't infer
1112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // either.
1113ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!maybe_parent.value().empty()) {
1114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string err_str;
1115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      style->parent = ResourceUtils::ParseStyleParentReference(
1116ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          maybe_parent.value(), &err_str);
1117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (!style->parent) {
1118ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(out_resource->source) << err_str);
11196f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski        return false;
1120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
11216f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Transform the namespace prefix to the actual package name, and mark the
1123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // reference as
1124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // private if appropriate.
1125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      TransformReferenceFromNamespace(parser, "", &style->parent.value());
11266f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
11276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
1129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // No parent was specified, so try inferring it from the style name.
1130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string style_name = out_resource->name.entry;
1131ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    size_t pos = style_name.find_last_of(u'.');
1132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (pos != std::string::npos) {
1133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      style->parent_inferred = true;
1134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      style->parent = Reference(
1135ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
11366f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
11386f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
1140ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
1141ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1142ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip text and comments.
1144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
11456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
11466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1147ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
1148ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
1149ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace == "" && element_name == "item") {
1150ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      error |= !ParseStyleItem(parser, style.get());
11511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1152ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1153ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1154ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << ":" << element_name << ">");
1155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
11566f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
1157cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
11586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
1160cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
11629ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
1163ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = std::move(style);
1164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
11656f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
11666f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1167d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskibool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1168d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  uint32_t resource_format = android::ResTable_map::TYPE_ANY;
1169d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
1170d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski    resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
1171d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski    if (resource_format == 0u) {
1172d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1173d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski                   << "'" << format_attr.value() << "' is an invalid format");
1174d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski      return false;
1175d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski    }
1176d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  }
1177d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  return ParseArrayImpl(parser, out_resource, resource_format);
11787ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
11797ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1180d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskibool ResourceParser::ParseIntegerArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1181d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_INTEGER);
11827ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
11837ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1184d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinskibool ResourceParser::ParseStringArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1185d5fd76a2ff78400505ade936fc36e707d69ecf72Adam Lesinski  return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_STRING);
11867ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski}
11877ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski
1188ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
1189ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                    ParsedResource* out_resource,
11907ff3ee19f4b831a526baf4b928d1ac172d070d82Adam Lesinski                                    const uint32_t typeMask) {
1191ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = ResourceType::kArray;
1192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Array> array = util::make_unique<Array>();
1194cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
11957542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  bool translatable = options_.translatable;
11967542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
11977542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
11987542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    if (!maybe_translatable) {
1199ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(out_resource->source)
1200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   << "invalid value for 'translatable'. Must be a boolean");
1201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
1202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
12037542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski    translatable = maybe_translatable.value();
1204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
12057542162cb1b1fd2ce8a26dd7f3fedc8de8160d38Adam Lesinski  array->SetTranslatable(translatable);
1206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
1208ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
1209ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1210ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip text and comments.
1212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1215ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const Source item_source = source_.WithLine(parser->line_number());
1216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
1217ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
1218ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace.empty() && element_name == "item") {
1219ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
1220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (!item) {
1221ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source) << "could not parse array item");
1222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1225ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      item->SetSource(item_source);
1226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      array->items.emplace_back(std::move(item));
1227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1228ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1230ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << "unknown tag <" << element_namespace << ":"
1231ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                   << element_name << ">");
1232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
1233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
1237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
12399ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
1240ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = std::move(array);
1241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
12426f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
12436f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1244ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
1245ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                 ParsedResource* out_resource) {
1246ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = ResourceType::kPlurals;
1247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1248cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Plural> plural = util::make_unique<Plural>();
1249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
1251ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
1252ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1253ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Skip text and comments.
1255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const Source item_source = source_.WithLine(parser->line_number());
1259ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
1260ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
1261ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace.empty() && element_name == "item") {
1262ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Maybe<StringPiece> maybe_quantity =
1263ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          xml::FindNonEmptyAttribute(parser, "quantity");
1264ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!maybe_quantity) {
1265ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
1266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "<item> in <plurals> requires attribute "
1267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "'quantity'");
1268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1272ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      StringPiece trimmed_quantity =
1273ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          util::TrimWhitespace(maybe_quantity.value());
1274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      size_t index = 0;
1275ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (trimmed_quantity == "zero") {
1276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::Zero;
1277ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (trimmed_quantity == "one") {
1278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::One;
1279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (trimmed_quantity == "two") {
1280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::Two;
1281ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (trimmed_quantity == "few") {
1282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::Few;
1283ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (trimmed_quantity == "many") {
1284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::Many;
1285ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      } else if (trimmed_quantity == "other") {
1286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        index = Plural::Other;
1287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
1288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
1289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "<item> in <plural> has invalid value '"
1290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                     << trimmed_quantity << "' for attribute 'quantity'");
1291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1295cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (plural->values[index]) {
1296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source) << "duplicate quantity '"
1297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                              << trimmed_quantity << "'");
1298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1302ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!(plural->values[index] = ParseXml(
1303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
1304cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1306ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      plural->values[index]->SetSource(item_source);
1307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1308ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1309ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(item_source) << "unknown tag <"
1310ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                            << element_namespace << ":"
1311ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                            << element_name << ">");
1312cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
1313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
1317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
13199ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
1320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = std::move(plural);
1321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
13226f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
13236f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
1325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                           ParsedResource* out_resource) {
1326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->name.type = ResourceType::kStyleable;
13276f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1328cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Declare-styleable is kPrivate by default, because it technically only
1329cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // exists in R.java.
1330ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->symbol_state = SymbolState::kPublic;
1331ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski
1332cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Declare-styleable only ends up in default config;
1333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (out_resource->config != ConfigDescription::DefaultConfig()) {
1334ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag_->Warn(DiagMessage(out_resource->source)
1335ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                << "ignoring configuration '" << out_resource->config
1336ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                << "' for styleable " << out_resource->name.entry);
1337ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_resource->config = ConfigDescription::DefaultConfig();
1338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
1341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string comment;
1343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
1344ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t depth = parser->depth();
1345ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1346ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (parser->event() == xml::XmlPullParser::Event::kComment) {
1347d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski      comment = util::TrimWhitespace(parser->comment()).to_string();
1348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1349ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Ignore text.
1351cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1352cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1354ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const Source item_source = source_.WithLine(parser->line_number());
1355ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_namespace = parser->element_namespace();
1356ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::string& element_name = parser->element_name();
1357ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (element_namespace.empty() && element_name == "attr") {
1358ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Maybe<StringPiece> maybe_name =
1359ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          xml::FindNonEmptyAttribute(parser, "name");
1360ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!maybe_name) {
1361ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source)
1362cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     << "<attr> tag must have a 'name' attribute");
1363cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // If this is a declaration, the package name may be in the name. Separate
1368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // these out.
1369cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Eg. <attr name="android:text" />
1370ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Maybe<Reference> maybe_ref =
1371ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          ResourceUtils::ParseXmlAttributeName(maybe_name.value());
1372ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!maybe_ref) {
1373ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
1374ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                              << maybe_name.value() << "'");
1375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1377cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1378cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1379ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Reference& child_ref = maybe_ref.value();
1380ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      xml::TransformReferenceFromNamespace(parser, "", &child_ref);
1381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Create the ParsedResource that will add the attribute to the table.
1383ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ParsedResource child_resource;
1384ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.name = child_ref.name.value();
1385ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.source = item_source;
1386ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_resource.comment = std::move(comment);
1387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1388ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!ParseAttrImpl(parser, &child_resource, true)) {
1389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
1390cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
1391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Create the reference to this attribute.
1394ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_ref.SetComment(child_resource.comment);
1395ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      child_ref.SetSource(item_source);
1396ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      styleable->entries.push_back(std::move(child_ref));
1397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1398ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_resource->child_resources.push_back(std::move(child_resource));
1399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1400ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1401ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag_->Error(DiagMessage(item_source) << "unknown tag <"
1402ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                            << element_namespace << ":"
1403ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                            << element_name << ">");
1404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      error = true;
1405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
1406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    comment = {};
1408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
1410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
1411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
1412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
14139ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
1414ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out_resource->value = std::move(styleable);
1415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
14166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
14176f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
1418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
1419