Home | History | Annotate | Download | only in aapt2
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "ResourceParser.h"
     18 
     19 #include <functional>
     20 #include <limits>
     21 #include <sstream>
     22 
     23 #include "android-base/logging.h"
     24 
     25 #include "ResourceTable.h"
     26 #include "ResourceUtils.h"
     27 #include "ResourceValues.h"
     28 #include "ValueVisitor.h"
     29 #include "text/Utf8Iterator.h"
     30 #include "util/ImmutableMap.h"
     31 #include "util/Maybe.h"
     32 #include "util/Util.h"
     33 #include "xml/XmlPullParser.h"
     34 
     35 using ::aapt::ResourceUtils::StringBuilder;
     36 using ::aapt::text::Utf8Iterator;
     37 using ::android::StringPiece;
     38 
     39 namespace aapt {
     40 
     41 constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
     42 
     43 // Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
     44 static bool ShouldIgnoreElement(const StringPiece& ns, const StringPiece& name) {
     45   return ns.empty() && (name == "skip" || name == "eat-comment");
     46 }
     47 
     48 static uint32_t ParseFormatTypeNoEnumsOrFlags(const StringPiece& piece) {
     49   if (piece == "reference") {
     50     return android::ResTable_map::TYPE_REFERENCE;
     51   } else if (piece == "string") {
     52     return android::ResTable_map::TYPE_STRING;
     53   } else if (piece == "integer") {
     54     return android::ResTable_map::TYPE_INTEGER;
     55   } else if (piece == "boolean") {
     56     return android::ResTable_map::TYPE_BOOLEAN;
     57   } else if (piece == "color") {
     58     return android::ResTable_map::TYPE_COLOR;
     59   } else if (piece == "float") {
     60     return android::ResTable_map::TYPE_FLOAT;
     61   } else if (piece == "dimension") {
     62     return android::ResTable_map::TYPE_DIMENSION;
     63   } else if (piece == "fraction") {
     64     return android::ResTable_map::TYPE_FRACTION;
     65   }
     66   return 0;
     67 }
     68 
     69 static uint32_t ParseFormatType(const StringPiece& piece) {
     70   if (piece == "enum") {
     71     return android::ResTable_map::TYPE_ENUM;
     72   } else if (piece == "flags") {
     73     return android::ResTable_map::TYPE_FLAGS;
     74   }
     75   return ParseFormatTypeNoEnumsOrFlags(piece);
     76 }
     77 
     78 static uint32_t ParseFormatAttribute(const StringPiece& str) {
     79   uint32_t mask = 0;
     80   for (StringPiece part : util::Tokenize(str, '|')) {
     81     StringPiece trimmed_part = util::TrimWhitespace(part);
     82     uint32_t type = ParseFormatType(trimmed_part);
     83     if (type == 0) {
     84       return 0;
     85     }
     86     mask |= type;
     87   }
     88   return mask;
     89 }
     90 
     91 // A parsed resource ready to be added to the ResourceTable.
     92 struct ParsedResource {
     93   ResourceName name;
     94   ConfigDescription config;
     95   std::string product;
     96   Source source;
     97 
     98   ResourceId id;
     99   Visibility::Level visibility_level = Visibility::Level::kUndefined;
    100   bool allow_new = false;
    101   bool overlayable = false;
    102 
    103   std::string comment;
    104   std::unique_ptr<Value> value;
    105   std::list<ParsedResource> child_resources;
    106 };
    107 
    108 // Recursively adds resources to the ResourceTable.
    109 static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
    110   StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
    111   if (trimmed_comment.size() != res->comment.size()) {
    112     // Only if there was a change do we re-assign.
    113     res->comment = trimmed_comment.to_string();
    114   }
    115 
    116   if (res->visibility_level != Visibility::Level::kUndefined) {
    117     Visibility visibility;
    118     visibility.level = res->visibility_level;
    119     visibility.source = res->source;
    120     visibility.comment = res->comment;
    121     if (!table->SetVisibilityWithId(res->name, visibility, res->id, diag)) {
    122       return false;
    123     }
    124   }
    125 
    126   if (res->allow_new) {
    127     AllowNew allow_new;
    128     allow_new.source = res->source;
    129     allow_new.comment = res->comment;
    130     if (!table->SetAllowNew(res->name, allow_new, diag)) {
    131       return false;
    132     }
    133   }
    134 
    135   if (res->overlayable) {
    136     Overlayable overlayable;
    137     overlayable.source = res->source;
    138     overlayable.comment = res->comment;
    139     if (!table->SetOverlayable(res->name, overlayable, diag)) {
    140       return false;
    141     }
    142   }
    143 
    144   if (res->value != nullptr) {
    145     // Attach the comment, source and config to the value.
    146     res->value->SetComment(std::move(res->comment));
    147     res->value->SetSource(std::move(res->source));
    148 
    149     if (!table->AddResourceWithId(res->name, res->id, res->config, res->product,
    150                                   std::move(res->value), diag)) {
    151       return false;
    152     }
    153   }
    154 
    155   bool error = false;
    156   for (ParsedResource& child : res->child_resources) {
    157     error |= !AddResourcesToTable(table, diag, &child);
    158   }
    159   return !error;
    160 }
    161 
    162 // Convenient aliases for more readable function calls.
    163 enum { kAllowRawString = true, kNoRawString = false };
    164 
    165 ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table,
    166                                const Source& source,
    167                                const ConfigDescription& config,
    168                                const ResourceParserOptions& options)
    169     : diag_(diag),
    170       table_(table),
    171       source_(source),
    172       config_(config),
    173       options_(options) {}
    174 
    175 // Base class Node for representing the various Spans and UntranslatableSections of an XML string.
    176 // This will be used to traverse and flatten the XML string into a single std::string, with all
    177 // Span and Untranslatable data maintained in parallel, as indices into the string.
    178 class Node {
    179  public:
    180   virtual ~Node() = default;
    181 
    182   // Adds the given child node to this parent node's set of child nodes, moving ownership to the
    183   // parent node as well.
    184   // Returns a pointer to the child node that was added as a convenience.
    185   template <typename T>
    186   T* AddChild(std::unique_ptr<T> node) {
    187     T* raw_ptr = node.get();
    188     children.push_back(std::move(node));
    189     return raw_ptr;
    190   }
    191 
    192   virtual void Build(StringBuilder* builder) const {
    193     for (const auto& child : children) {
    194       child->Build(builder);
    195     }
    196   }
    197 
    198   std::vector<std::unique_ptr<Node>> children;
    199 };
    200 
    201 // A chunk of text in the XML string. This lives between other tags, such as XLIFF tags and Spans.
    202 class SegmentNode : public Node {
    203  public:
    204   std::string data;
    205 
    206   void Build(StringBuilder* builder) const override {
    207     builder->AppendText(data);
    208   }
    209 };
    210 
    211 // A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
    212 class SpanNode : public Node {
    213  public:
    214   std::string name;
    215 
    216   void Build(StringBuilder* builder) const override {
    217     StringBuilder::SpanHandle span_handle = builder->StartSpan(name);
    218     Node::Build(builder);
    219     builder->EndSpan(span_handle);
    220   }
    221 };
    222 
    223 // An XLIFF 'g' tag, which marks a section of the string as untranslatable.
    224 class UntranslatableNode : public Node {
    225  public:
    226   void Build(StringBuilder* builder) const override {
    227     StringBuilder::UntranslatableHandle handle = builder->StartUntranslatable();
    228     Node::Build(builder);
    229     builder->EndUntranslatable(handle);
    230   }
    231 };
    232 
    233 // Build a string from XML that converts nested elements into Span objects.
    234 bool ResourceParser::FlattenXmlSubtree(
    235     xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
    236     std::vector<UntranslatableSection>* out_untranslatable_sections) {
    237   std::string raw_string;
    238   std::string current_text;
    239 
    240   // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
    241   Maybe<size_t> untranslatable_start_depth;
    242 
    243   Node root;
    244   std::vector<Node*> node_stack;
    245   node_stack.push_back(&root);
    246 
    247   bool saw_span_node = false;
    248   SegmentNode* first_segment = nullptr;
    249   SegmentNode* last_segment = nullptr;
    250 
    251   size_t depth = 1;
    252   while (depth > 0 && xml::XmlPullParser::IsGoodEvent(parser->Next())) {
    253     const xml::XmlPullParser::Event event = parser->event();
    254 
    255     // First take care of any SegmentNodes that should be created.
    256     if (event == xml::XmlPullParser::Event::kStartElement ||
    257         event == xml::XmlPullParser::Event::kEndElement) {
    258       if (!current_text.empty()) {
    259         std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
    260         segment_node->data = std::move(current_text);
    261         last_segment = node_stack.back()->AddChild(std::move(segment_node));
    262         if (first_segment == nullptr) {
    263           first_segment = last_segment;
    264         }
    265         current_text = {};
    266       }
    267     }
    268 
    269     switch (event) {
    270       case xml::XmlPullParser::Event::kText: {
    271         current_text += parser->text();
    272         raw_string += parser->text();
    273       } break;
    274 
    275       case xml::XmlPullParser::Event::kStartElement: {
    276         if (parser->element_namespace().empty()) {
    277           // This is an HTML tag which we encode as a span. Add it to the span stack.
    278           std::unique_ptr<SpanNode> span_node = util::make_unique<SpanNode>();
    279           span_node->name = parser->element_name();
    280           const auto end_attr_iter = parser->end_attributes();
    281           for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter;
    282                ++attr_iter) {
    283             span_node->name += ";";
    284             span_node->name += attr_iter->name;
    285             span_node->name += "=";
    286             span_node->name += attr_iter->value;
    287           }
    288 
    289           node_stack.push_back(node_stack.back()->AddChild(std::move(span_node)));
    290           saw_span_node = true;
    291         } else if (parser->element_namespace() == sXliffNamespaceUri) {
    292           // This is an XLIFF tag, which is not encoded as a span.
    293           if (parser->element_name() == "g") {
    294             // Check that an 'untranslatable' tag is not already being processed. Nested
    295             // <xliff:g> tags are illegal.
    296             if (untranslatable_start_depth) {
    297               diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    298                            << "illegal nested XLIFF 'g' tag");
    299               return false;
    300             } else {
    301               // Mark the beginning of an 'untranslatable' section.
    302               untranslatable_start_depth = depth;
    303               node_stack.push_back(
    304                   node_stack.back()->AddChild(util::make_unique<UntranslatableNode>()));
    305             }
    306           } else {
    307             // Ignore unknown XLIFF tags, but don't warn.
    308             node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
    309           }
    310         } else {
    311           // Besides XLIFF, any other namespaced tag is unsupported and ignored.
    312           diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
    313                       << "ignoring element '" << parser->element_name()
    314                       << "' with unknown namespace '" << parser->element_namespace() << "'");
    315           node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
    316         }
    317 
    318         // Enter one level inside the element.
    319         depth++;
    320       } break;
    321 
    322       case xml::XmlPullParser::Event::kEndElement: {
    323         // Return one level from within the element.
    324         depth--;
    325         if (depth == 0) {
    326           break;
    327         }
    328 
    329         node_stack.pop_back();
    330         if (untranslatable_start_depth == make_value(depth)) {
    331           // This is the end of an untranslatable section.
    332           untranslatable_start_depth = {};
    333         }
    334       } break;
    335 
    336       default:
    337         // ignore.
    338         break;
    339     }
    340   }
    341 
    342   // Sanity check to make sure we processed all the nodes.
    343   CHECK(node_stack.size() == 1u);
    344   CHECK(node_stack.back() == &root);
    345 
    346   if (!saw_span_node) {
    347     // If there were no spans, we must treat this string a little differently (according to AAPT).
    348     // Find and strip the leading whitespace from the first segment, and the trailing whitespace
    349     // from the last segment.
    350     if (first_segment != nullptr) {
    351       // Trim leading whitespace.
    352       StringPiece trimmed = util::TrimLeadingWhitespace(first_segment->data);
    353       if (trimmed.size() != first_segment->data.size()) {
    354         first_segment->data = trimmed.to_string();
    355       }
    356     }
    357 
    358     if (last_segment != nullptr) {
    359       // Trim trailing whitespace.
    360       StringPiece trimmed = util::TrimTrailingWhitespace(last_segment->data);
    361       if (trimmed.size() != last_segment->data.size()) {
    362         last_segment->data = trimmed.to_string();
    363       }
    364     }
    365   }
    366 
    367   // Have the XML structure flatten itself into the StringBuilder. The StringBuilder will take
    368   // care of recording the correctly adjusted Spans and UntranslatableSections.
    369   StringBuilder builder;
    370   root.Build(&builder);
    371   if (!builder) {
    372     diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) << builder.GetError());
    373     return false;
    374   }
    375 
    376   ResourceUtils::FlattenedXmlString flattened_string = builder.GetFlattenedString();
    377   *out_raw_string = std::move(raw_string);
    378   *out_untranslatable_sections = std::move(flattened_string.untranslatable_sections);
    379   out_style_string->str = std::move(flattened_string.text);
    380   out_style_string->spans = std::move(flattened_string.spans);
    381   return true;
    382 }
    383 
    384 bool ResourceParser::Parse(xml::XmlPullParser* parser) {
    385   bool error = false;
    386   const size_t depth = parser->depth();
    387   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
    388     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
    389       // Skip comments and text.
    390       continue;
    391     }
    392 
    393     if (!parser->element_namespace().empty() || parser->element_name() != "resources") {
    394       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    395                    << "root element must be <resources>");
    396       return false;
    397     }
    398 
    399     error |= !ParseResources(parser);
    400     break;
    401   };
    402 
    403   if (parser->event() == xml::XmlPullParser::Event::kBadDocument) {
    404     diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    405                  << "xml parser error: " << parser->error());
    406     return false;
    407   }
    408   return !error;
    409 }
    410 
    411 bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
    412   std::set<ResourceName> stripped_resources;
    413 
    414   bool error = false;
    415   std::string comment;
    416   const size_t depth = parser->depth();
    417   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
    418     const xml::XmlPullParser::Event event = parser->event();
    419     if (event == xml::XmlPullParser::Event::kComment) {
    420       comment = parser->comment();
    421       continue;
    422     }
    423 
    424     if (event == xml::XmlPullParser::Event::kText) {
    425       if (!util::TrimWhitespace(parser->text()).empty()) {
    426         diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    427                      << "plain text not allowed here");
    428         error = true;
    429       }
    430       continue;
    431     }
    432 
    433     CHECK(event == xml::XmlPullParser::Event::kStartElement);
    434 
    435     if (!parser->element_namespace().empty()) {
    436       // Skip unknown namespace.
    437       continue;
    438     }
    439 
    440     std::string element_name = parser->element_name();
    441     if (element_name == "skip" || element_name == "eat-comment") {
    442       comment = "";
    443       continue;
    444     }
    445 
    446     ParsedResource parsed_resource;
    447     parsed_resource.config = config_;
    448     parsed_resource.source = source_.WithLine(parser->line_number());
    449     parsed_resource.comment = std::move(comment);
    450 
    451     // Extract the product name if it exists.
    452     if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
    453       parsed_resource.product = maybe_product.value().to_string();
    454     }
    455 
    456     // Parse the resource regardless of product.
    457     if (!ParseResource(parser, &parsed_resource)) {
    458       error = true;
    459       continue;
    460     }
    461 
    462     if (!AddResourcesToTable(table_, diag_, &parsed_resource)) {
    463       error = true;
    464     }
    465   }
    466 
    467   // Check that we included at least one variant of each stripped resource.
    468   for (const ResourceName& stripped_resource : stripped_resources) {
    469     if (!table_->FindResource(stripped_resource)) {
    470       // Failed to find the resource.
    471       diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource
    472                                         << "' was filtered out but no product variant remains");
    473       error = true;
    474     }
    475   }
    476 
    477   return !error;
    478 }
    479 
    480 bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
    481                                    ParsedResource* out_resource) {
    482   struct ItemTypeFormat {
    483     ResourceType type;
    484     uint32_t format;
    485   };
    486 
    487   using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
    488                                           ParsedResource*)>;
    489 
    490   static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({
    491       {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}},
    492       {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}},
    493       {"configVarying", {ResourceType::kConfigVarying, android::ResTable_map::TYPE_ANY}},
    494       {"dimen",
    495        {ResourceType::kDimen,
    496         android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
    497             android::ResTable_map::TYPE_DIMENSION}},
    498       {"drawable", {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}},
    499       {"fraction",
    500        {ResourceType::kFraction,
    501         android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
    502             android::ResTable_map::TYPE_DIMENSION}},
    503       {"integer", {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
    504       {"string", {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
    505   });
    506 
    507   static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({
    508       {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)},
    509       {"array", std::mem_fn(&ResourceParser::ParseArray)},
    510       {"attr", std::mem_fn(&ResourceParser::ParseAttr)},
    511       {"configVarying",
    512        std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kConfigVarying,
    513                  std::placeholders::_2, std::placeholders::_3)},
    514       {"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
    515       {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
    516       {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
    517       {"overlayable", std::mem_fn(&ResourceParser::ParseOverlayable)},
    518       {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
    519       {"public", std::mem_fn(&ResourceParser::ParsePublic)},
    520       {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
    521       {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
    522       {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
    523                           std::placeholders::_2, std::placeholders::_3)},
    524       {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
    525   });
    526 
    527   std::string resource_type = parser->element_name();
    528 
    529   // The value format accepted for this resource.
    530   uint32_t resource_format = 0u;
    531 
    532   bool can_be_item = true;
    533   bool can_be_bag = true;
    534   if (resource_type == "item") {
    535     can_be_bag = false;
    536 
    537     // The default format for <item> is any. If a format attribute is present, that one will
    538     // override the default.
    539     resource_format = android::ResTable_map::TYPE_ANY;
    540 
    541     // Items have their type encoded in the type attribute.
    542     if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
    543       resource_type = maybe_type.value().to_string();
    544     } else {
    545       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    546                    << "<item> must have a 'type' attribute");
    547       return false;
    548     }
    549 
    550     if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
    551       // An explicit format for this resource was specified. The resource will
    552       // retain its type in its name, but the accepted value for this type is
    553       // overridden.
    554       resource_format = ParseFormatTypeNoEnumsOrFlags(maybe_format.value());
    555       if (!resource_format) {
    556         diag_->Error(DiagMessage(out_resource->source)
    557                      << "'" << maybe_format.value()
    558                      << "' is an invalid format");
    559         return false;
    560       }
    561     }
    562   } else if (resource_type == "bag") {
    563     can_be_item = false;
    564 
    565     // Bags have their type encoded in the type attribute.
    566     if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
    567       resource_type = maybe_type.value().to_string();
    568     } else {
    569       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
    570                    << "<bag> must have a 'type' attribute");
    571       return false;
    572     }
    573   }
    574 
    575   // Get the name of the resource. This will be checked later, because not all
    576   // XML elements require a name.
    577   Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
    578 
    579   if (resource_type == "id") {
    580     if (!maybe_name) {
    581       diag_->Error(DiagMessage(out_resource->source)
    582                    << "<" << parser->element_name()
    583                    << "> missing 'name' attribute");
    584       return false;
    585     }
    586 
    587     out_resource->name.type = ResourceType::kId;
    588     out_resource->name.entry = maybe_name.value().to_string();
    589 
    590     // Ids either represent a unique resource id or reference another resource id
    591     auto item = ParseItem(parser, out_resource, resource_format);
    592     if (!item) {
    593       return false;
    594     }
    595 
    596     String* empty = ValueCast<String>(out_resource->value.get());
    597     if (empty && *empty->value == "") {
    598       // If no inner element exists, represent a unique identifier
    599       out_resource->value = util::make_unique<Id>();
    600     } else {
    601       Reference* ref = ValueCast<Reference>(out_resource->value.get());
    602       if (ref && !ref->name && !ref->id) {
    603         // A null reference also means there is no inner element when ids are in the form:
    604         //    <id name="name"/>
    605         out_resource->value = util::make_unique<Id>();
    606       } else if (!ref || ref->name.value().type != ResourceType::kId) {
    607         // If an inner element exists, the inner element must be a reference to another resource id
    608         diag_->Error(DiagMessage(out_resource->source)
    609                          << "<" << parser->element_name()
    610                          << "> inner element must either be a resource reference or empty");
    611         return false;
    612       }
    613     }
    614 
    615     return true;
    616   }
    617 
    618   if (can_be_item) {
    619     const auto item_iter = elToItemMap.find(resource_type);
    620     if (item_iter != elToItemMap.end()) {
    621       // This is an item, record its type and format and start parsing.
    622 
    623       if (!maybe_name) {
    624         diag_->Error(DiagMessage(out_resource->source)
    625                      << "<" << parser->element_name() << "> missing 'name' attribute");
    626         return false;
    627       }
    628 
    629       out_resource->name.type = item_iter->second.type;
    630       out_resource->name.entry = maybe_name.value().to_string();
    631 
    632       // Only use the implied format of the type when there is no explicit format.
    633       if (resource_format == 0u) {
    634         resource_format = item_iter->second.format;
    635       }
    636 
    637       if (!ParseItem(parser, out_resource, resource_format)) {
    638         return false;
    639       }
    640       return true;
    641     }
    642   }
    643 
    644   // This might be a bag or something.
    645   if (can_be_bag) {
    646     const auto bag_iter = elToBagMap.find(resource_type);
    647     if (bag_iter != elToBagMap.end()) {
    648       // Ensure we have a name (unless this is a <public-group>).
    649       if (resource_type != "public-group" && resource_type != "overlayable") {
    650         if (!maybe_name) {
    651           diag_->Error(DiagMessage(out_resource->source)
    652                        << "<" << parser->element_name() << "> missing 'name' attribute");
    653           return false;
    654         }
    655 
    656         out_resource->name.entry = maybe_name.value().to_string();
    657       }
    658 
    659       // Call the associated parse method. The type will be filled in by the
    660       // parse func.
    661       if (!bag_iter->second(this, parser, out_resource)) {
    662         return false;
    663       }
    664       return true;
    665     }
    666   }
    667 
    668   if (can_be_item) {
    669     // Try parsing the elementName (or type) as a resource. These shall only be
    670     // resources like 'layout' or 'xml' and they can only be references.
    671     const ResourceType* parsed_type = ParseResourceType(resource_type);
    672     if (parsed_type) {
    673       if (!maybe_name) {
    674         diag_->Error(DiagMessage(out_resource->source)
    675                      << "<" << parser->element_name()
    676                      << "> missing 'name' attribute");
    677         return false;
    678       }
    679 
    680       out_resource->name.type = *parsed_type;
    681       out_resource->name.entry = maybe_name.value().to_string();
    682       out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
    683       if (!out_resource->value) {
    684         diag_->Error(DiagMessage(out_resource->source)
    685                      << "invalid value for type '" << *parsed_type << "'. Expected a reference");
    686         return false;
    687       }
    688       return true;
    689     }
    690   }
    691 
    692   diag_->Warn(DiagMessage(out_resource->source)
    693               << "unknown resource type '" << parser->element_name() << "'");
    694   return false;
    695 }
    696 
    697 bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
    698                                ParsedResource* out_resource,
    699                                const uint32_t format) {
    700   if (format == android::ResTable_map::TYPE_STRING) {
    701     return ParseString(parser, out_resource);
    702   }
    703 
    704   out_resource->value = ParseXml(parser, format, kNoRawString);
    705   if (!out_resource->value) {
    706     diag_->Error(DiagMessage(out_resource->source) << "invalid "
    707                                                    << out_resource->name.type);
    708     return false;
    709   }
    710   return true;
    711 }
    712 
    713 /**
    714  * Reads the entire XML subtree and attempts to parse it as some Item,
    715  * with typeMask denoting which items it can be. If allowRawValue is
    716  * true, a RawString is returned if the XML couldn't be parsed as
    717  * an Item. If allowRawValue is false, nullptr is returned in this
    718  * case.
    719  */
    720 std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
    721                                                const uint32_t type_mask,
    722                                                const bool allow_raw_value) {
    723   const size_t begin_xml_line = parser->line_number();
    724 
    725   std::string raw_value;
    726   StyleString style_string;
    727   std::vector<UntranslatableSection> untranslatable_sections;
    728   if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
    729     return {};
    730   }
    731 
    732   if (!style_string.spans.empty()) {
    733     // This can only be a StyledString.
    734     std::unique_ptr<StyledString> styled_string =
    735         util::make_unique<StyledString>(table_->string_pool.MakeRef(
    736             style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
    737     styled_string->untranslatable_sections = std::move(untranslatable_sections);
    738     return std::move(styled_string);
    739   }
    740 
    741   auto on_create_reference = [&](const ResourceName& name) {
    742     // name.package can be empty here, as it will assume the package name of the
    743     // table.
    744     std::unique_ptr<Id> id = util::make_unique<Id>();
    745     id->SetSource(source_.WithLine(begin_xml_line));
    746     table_->AddResource(name, {}, {}, std::move(id), diag_);
    747   };
    748 
    749   // Process the raw value.
    750   std::unique_ptr<Item> processed_item =
    751       ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
    752   if (processed_item) {
    753     // Fix up the reference.
    754     if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
    755       ResolvePackage(parser, ref);
    756     }
    757     return processed_item;
    758   }
    759 
    760   // Try making a regular string.
    761   if (type_mask & android::ResTable_map::TYPE_STRING) {
    762     // Use the trimmed, escaped string.
    763     std::unique_ptr<String> string = util::make_unique<String>(
    764         table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
    765     string->untranslatable_sections = std::move(untranslatable_sections);
    766     return std::move(string);
    767   }
    768 
    769   // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
    770   if (util::TrimWhitespace(raw_value).empty()) {
    771     return ResourceUtils::MakeNull();
    772   }
    773 
    774   if (allow_raw_value) {
    775     // We can't parse this so return a RawString if we are allowed.
    776     return util::make_unique<RawString>(
    777         table_->string_pool.MakeRef(raw_value, StringPool::Context(config_)));
    778   }
    779   return {};
    780 }
    781 
    782 bool ResourceParser::ParseString(xml::XmlPullParser* parser,
    783                                  ParsedResource* out_resource) {
    784   bool formatted = true;
    785   if (Maybe<StringPiece> formatted_attr =
    786           xml::FindAttribute(parser, "formatted")) {
    787     Maybe<bool> maybe_formatted =
    788         ResourceUtils::ParseBool(formatted_attr.value());
    789     if (!maybe_formatted) {
    790       diag_->Error(DiagMessage(out_resource->source)
    791                    << "invalid value for 'formatted'. Must be a boolean");
    792       return false;
    793     }
    794     formatted = maybe_formatted.value();
    795   }
    796 
    797   bool translatable = options_.translatable;
    798   if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
    799     Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
    800     if (!maybe_translatable) {
    801       diag_->Error(DiagMessage(out_resource->source)
    802                    << "invalid value for 'translatable'. Must be a boolean");
    803       return false;
    804     }
    805     translatable = maybe_translatable.value();
    806   }
    807 
    808   out_resource->value =
    809       ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
    810   if (!out_resource->value) {
    811     diag_->Error(DiagMessage(out_resource->source) << "not a valid string");
    812     return false;
    813   }
    814 
    815   if (String* string_value = ValueCast<String>(out_resource->value.get())) {
    816     string_value->SetTranslatable(translatable);
    817 
    818     if (formatted && translatable) {
    819       if (!util::VerifyJavaStringFormat(*string_value->value)) {
    820         DiagMessage msg(out_resource->source);
    821         msg << "multiple substitutions specified in non-positional format; "
    822                "did you mean to add the formatted=\"false\" attribute?";
    823         if (options_.error_on_positional_arguments) {
    824           diag_->Error(msg);
    825           return false;
    826         }
    827 
    828         diag_->Warn(msg);
    829       }
    830     }
    831 
    832   } else if (StyledString* string_value = ValueCast<StyledString>(out_resource->value.get())) {
    833     string_value->SetTranslatable(translatable);
    834   }
    835   return true;
    836 }
    837 
    838 bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
    839   if (out_resource->config != ConfigDescription::DefaultConfig()) {
    840     diag_->Warn(DiagMessage(out_resource->source)
    841                 << "ignoring configuration '" << out_resource->config << "' for <public> tag");
    842   }
    843 
    844   Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
    845   if (!maybe_type) {
    846     diag_->Error(DiagMessage(out_resource->source)
    847                  << "<public> must have a 'type' attribute");
    848     return false;
    849   }
    850 
    851   const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
    852   if (!parsed_type) {
    853     diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
    854                                                    << maybe_type.value()
    855                                                    << "' in <public>");
    856     return false;
    857   }
    858 
    859   out_resource->name.type = *parsed_type;
    860 
    861   if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
    862     Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
    863     if (!maybe_id) {
    864       diag_->Error(DiagMessage(out_resource->source)
    865                    << "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
    866       return false;
    867     }
    868     out_resource->id = maybe_id.value();
    869   }
    870 
    871   if (*parsed_type == ResourceType::kId) {
    872     // An ID marked as public is also the definition of an ID.
    873     out_resource->value = util::make_unique<Id>();
    874   }
    875 
    876   out_resource->visibility_level = Visibility::Level::kPublic;
    877   return true;
    878 }
    879 
    880 bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
    881   if (out_resource->config != ConfigDescription::DefaultConfig()) {
    882     diag_->Warn(DiagMessage(out_resource->source)
    883                 << "ignoring configuration '" << out_resource->config
    884                 << "' for <public-group> tag");
    885   }
    886 
    887   Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
    888   if (!maybe_type) {
    889     diag_->Error(DiagMessage(out_resource->source)
    890                  << "<public-group> must have a 'type' attribute");
    891     return false;
    892   }
    893 
    894   const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
    895   if (!parsed_type) {
    896     diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
    897                                                    << maybe_type.value()
    898                                                    << "' in <public-group>");
    899     return false;
    900   }
    901 
    902   Maybe<StringPiece> maybe_id_str =
    903       xml::FindNonEmptyAttribute(parser, "first-id");
    904   if (!maybe_id_str) {
    905     diag_->Error(DiagMessage(out_resource->source)
    906                  << "<public-group> must have a 'first-id' attribute");
    907     return false;
    908   }
    909 
    910   Maybe<ResourceId> maybe_id =
    911       ResourceUtils::ParseResourceId(maybe_id_str.value());
    912   if (!maybe_id) {
    913     diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
    914                                                    << maybe_id_str.value()
    915                                                    << "' in <public-group>");
    916     return false;
    917   }
    918 
    919   ResourceId next_id = maybe_id.value();
    920 
    921   std::string comment;
    922   bool error = false;
    923   const size_t depth = parser->depth();
    924   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
    925     if (parser->event() == xml::XmlPullParser::Event::kComment) {
    926       comment = util::TrimWhitespace(parser->comment()).to_string();
    927       continue;
    928     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
    929       // Skip text.
    930       continue;
    931     }
    932 
    933     const Source item_source = source_.WithLine(parser->line_number());
    934     const std::string& element_namespace = parser->element_namespace();
    935     const std::string& element_name = parser->element_name();
    936     if (element_namespace.empty() && element_name == "public") {
    937       Maybe<StringPiece> maybe_name =
    938           xml::FindNonEmptyAttribute(parser, "name");
    939       if (!maybe_name) {
    940         diag_->Error(DiagMessage(item_source)
    941                      << "<public> must have a 'name' attribute");
    942         error = true;
    943         continue;
    944       }
    945 
    946       if (xml::FindNonEmptyAttribute(parser, "id")) {
    947         diag_->Error(DiagMessage(item_source)
    948                      << "'id' is ignored within <public-group>");
    949         error = true;
    950         continue;
    951       }
    952 
    953       if (xml::FindNonEmptyAttribute(parser, "type")) {
    954         diag_->Error(DiagMessage(item_source)
    955                      << "'type' is ignored within <public-group>");
    956         error = true;
    957         continue;
    958       }
    959 
    960       ParsedResource child_resource;
    961       child_resource.name.type = *parsed_type;
    962       child_resource.name.entry = maybe_name.value().to_string();
    963       child_resource.id = next_id;
    964       child_resource.comment = std::move(comment);
    965       child_resource.source = item_source;
    966       child_resource.visibility_level = Visibility::Level::kPublic;
    967       out_resource->child_resources.push_back(std::move(child_resource));
    968 
    969       next_id.id += 1;
    970 
    971     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
    972       diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
    973       error = true;
    974     }
    975   }
    976   return !error;
    977 }
    978 
    979 bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
    980                                      ParsedResource* out_resource) {
    981   Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
    982   if (!maybe_type) {
    983     diag_->Error(DiagMessage(out_resource->source)
    984                  << "<" << parser->element_name()
    985                  << "> must have a 'type' attribute");
    986     return false;
    987   }
    988 
    989   const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
    990   if (!parsed_type) {
    991     diag_->Error(DiagMessage(out_resource->source)
    992                  << "invalid resource type '" << maybe_type.value() << "' in <"
    993                  << parser->element_name() << ">");
    994     return false;
    995   }
    996 
    997   out_resource->name.type = *parsed_type;
    998   return true;
    999 }
   1000 
   1001 bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1002   if (out_resource->config != ConfigDescription::DefaultConfig()) {
   1003     diag_->Warn(DiagMessage(out_resource->source)
   1004                 << "ignoring configuration '" << out_resource->config << "' for <"
   1005                 << parser->element_name() << "> tag");
   1006   }
   1007 
   1008   if (!ParseSymbolImpl(parser, out_resource)) {
   1009     return false;
   1010   }
   1011 
   1012   out_resource->visibility_level = Visibility::Level::kPrivate;
   1013   return true;
   1014 }
   1015 
   1016 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1017   if (out_resource->config != ConfigDescription::DefaultConfig()) {
   1018     diag_->Warn(DiagMessage(out_resource->source)
   1019                 << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
   1020   }
   1021 
   1022   if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
   1023     const StringPiece& policy = maybe_policy.value();
   1024     if (policy != "system") {
   1025       diag_->Error(DiagMessage(out_resource->source)
   1026                    << "<overlayable> has invalid policy '" << policy << "'");
   1027       return false;
   1028     }
   1029   }
   1030 
   1031   bool error = false;
   1032   const size_t depth = parser->depth();
   1033   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1034     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1035       // Skip text/comments.
   1036       continue;
   1037     }
   1038 
   1039     const Source item_source = source_.WithLine(parser->line_number());
   1040     const std::string& element_namespace = parser->element_namespace();
   1041     const std::string& element_name = parser->element_name();
   1042     if (element_namespace.empty() && element_name == "item") {
   1043       Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   1044       if (!maybe_name) {
   1045         diag_->Error(DiagMessage(item_source)
   1046                      << "<item> within an <overlayable> tag must have a 'name' attribute");
   1047         error = true;
   1048         continue;
   1049       }
   1050 
   1051       Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
   1052       if (!maybe_type) {
   1053         diag_->Error(DiagMessage(item_source)
   1054                      << "<item> within an <overlayable> tag must have a 'type' attribute");
   1055         error = true;
   1056         continue;
   1057       }
   1058 
   1059       const ResourceType* type = ParseResourceType(maybe_type.value());
   1060       if (type == nullptr) {
   1061         diag_->Error(DiagMessage(out_resource->source)
   1062                      << "invalid resource type '" << maybe_type.value()
   1063                      << "' in <item> within an <overlayable>");
   1064         error = true;
   1065         continue;
   1066       }
   1067 
   1068       ParsedResource child_resource;
   1069       child_resource.name.type = *type;
   1070       child_resource.name.entry = maybe_name.value().to_string();
   1071       child_resource.source = item_source;
   1072       child_resource.overlayable = true;
   1073       out_resource->child_resources.push_back(std::move(child_resource));
   1074 
   1075       xml::XmlPullParser::SkipCurrentElement(parser);
   1076     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1077       diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
   1078       error = true;
   1079     }
   1080   }
   1081   return !error;
   1082 }
   1083 
   1084 bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1085   if (ParseSymbolImpl(parser, out_resource)) {
   1086     out_resource->visibility_level = Visibility::Level::kUndefined;
   1087     out_resource->allow_new = true;
   1088     return true;
   1089   }
   1090   return false;
   1091 }
   1092 
   1093 bool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
   1094                                ParsedResource* out_resource) {
   1095   return ParseAttrImpl(parser, out_resource, false);
   1096 }
   1097 
   1098 bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
   1099                                    ParsedResource* out_resource, bool weak) {
   1100   out_resource->name.type = ResourceType::kAttr;
   1101 
   1102   // Attributes only end up in default configuration.
   1103   if (out_resource->config != ConfigDescription::DefaultConfig()) {
   1104     diag_->Warn(DiagMessage(out_resource->source)
   1105                 << "ignoring configuration '" << out_resource->config
   1106                 << "' for attribute " << out_resource->name);
   1107     out_resource->config = ConfigDescription::DefaultConfig();
   1108   }
   1109 
   1110   uint32_t type_mask = 0;
   1111 
   1112   Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
   1113   if (maybe_format) {
   1114     type_mask = ParseFormatAttribute(maybe_format.value());
   1115     if (type_mask == 0) {
   1116       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1117                    << "invalid attribute format '" << maybe_format.value() << "'");
   1118       return false;
   1119     }
   1120   }
   1121 
   1122   Maybe<int32_t> maybe_min, maybe_max;
   1123 
   1124   if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
   1125     StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
   1126     if (!min_str.empty()) {
   1127       std::u16string min_str16 = util::Utf8ToUtf16(min_str);
   1128       android::Res_value value;
   1129       if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(), &value)) {
   1130         maybe_min = static_cast<int32_t>(value.data);
   1131       }
   1132     }
   1133 
   1134     if (!maybe_min) {
   1135       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1136                    << "invalid 'min' value '" << min_str << "'");
   1137       return false;
   1138     }
   1139   }
   1140 
   1141   if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
   1142     StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
   1143     if (!max_str.empty()) {
   1144       std::u16string max_str16 = util::Utf8ToUtf16(max_str);
   1145       android::Res_value value;
   1146       if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(), &value)) {
   1147         maybe_max = static_cast<int32_t>(value.data);
   1148       }
   1149     }
   1150 
   1151     if (!maybe_max) {
   1152       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1153                    << "invalid 'max' value '" << max_str << "'");
   1154       return false;
   1155     }
   1156   }
   1157 
   1158   if ((maybe_min || maybe_max) &&
   1159       (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) {
   1160     diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1161                  << "'min' and 'max' can only be used when format='integer'");
   1162     return false;
   1163   }
   1164 
   1165   struct SymbolComparator {
   1166     bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) const {
   1167       return a.symbol.name.value() < b.symbol.name.value();
   1168     }
   1169   };
   1170 
   1171   std::set<Attribute::Symbol, SymbolComparator> items;
   1172 
   1173   std::string comment;
   1174   bool error = false;
   1175   const size_t depth = parser->depth();
   1176   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1177     if (parser->event() == xml::XmlPullParser::Event::kComment) {
   1178       comment = util::TrimWhitespace(parser->comment()).to_string();
   1179       continue;
   1180     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1181       // Skip text.
   1182       continue;
   1183     }
   1184 
   1185     const Source item_source = source_.WithLine(parser->line_number());
   1186     const std::string& element_namespace = parser->element_namespace();
   1187     const std::string& element_name = parser->element_name();
   1188     if (element_namespace.empty() && (element_name == "flag" || element_name == "enum")) {
   1189       if (element_name == "enum") {
   1190         if (type_mask & android::ResTable_map::TYPE_FLAGS) {
   1191           diag_->Error(DiagMessage(item_source)
   1192                        << "can not define an <enum>; already defined a <flag>");
   1193           error = true;
   1194           continue;
   1195         }
   1196         type_mask |= android::ResTable_map::TYPE_ENUM;
   1197 
   1198       } else if (element_name == "flag") {
   1199         if (type_mask & android::ResTable_map::TYPE_ENUM) {
   1200           diag_->Error(DiagMessage(item_source)
   1201                        << "can not define a <flag>; already defined an <enum>");
   1202           error = true;
   1203           continue;
   1204         }
   1205         type_mask |= android::ResTable_map::TYPE_FLAGS;
   1206       }
   1207 
   1208       if (Maybe<Attribute::Symbol> s =
   1209               ParseEnumOrFlagItem(parser, element_name)) {
   1210         Attribute::Symbol& symbol = s.value();
   1211         ParsedResource child_resource;
   1212         child_resource.name = symbol.symbol.name.value();
   1213         child_resource.source = item_source;
   1214         child_resource.value = util::make_unique<Id>();
   1215         out_resource->child_resources.push_back(std::move(child_resource));
   1216 
   1217         symbol.symbol.SetComment(std::move(comment));
   1218         symbol.symbol.SetSource(item_source);
   1219 
   1220         auto insert_result = items.insert(std::move(symbol));
   1221         if (!insert_result.second) {
   1222           const Attribute::Symbol& existing_symbol = *insert_result.first;
   1223           diag_->Error(DiagMessage(item_source)
   1224                        << "duplicate symbol '"
   1225                        << existing_symbol.symbol.name.value().entry << "'");
   1226 
   1227           diag_->Note(DiagMessage(existing_symbol.symbol.GetSource())
   1228                       << "first defined here");
   1229           error = true;
   1230         }
   1231       } else {
   1232         error = true;
   1233       }
   1234     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1235       diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
   1236       error = true;
   1237     }
   1238 
   1239     comment = {};
   1240   }
   1241 
   1242   if (error) {
   1243     return false;
   1244   }
   1245 
   1246   std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(
   1247       type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
   1248   attr->SetWeak(weak);
   1249   attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
   1250   attr->min_int = maybe_min.value_or_default(std::numeric_limits<int32_t>::min());
   1251   attr->max_int = maybe_max.value_or_default(std::numeric_limits<int32_t>::max());
   1252   out_resource->value = std::move(attr);
   1253   return true;
   1254 }
   1255 
   1256 Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
   1257     xml::XmlPullParser* parser, const StringPiece& tag) {
   1258   const Source source = source_.WithLine(parser->line_number());
   1259 
   1260   Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   1261   if (!maybe_name) {
   1262     diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
   1263                                      << tag << ">");
   1264     return {};
   1265   }
   1266 
   1267   Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
   1268   if (!maybe_value) {
   1269     diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
   1270                                      << tag << ">");
   1271     return {};
   1272   }
   1273 
   1274   std::u16string value16 = util::Utf8ToUtf16(maybe_value.value());
   1275   android::Res_value val;
   1276   if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
   1277     diag_->Error(DiagMessage(source) << "invalid value '" << maybe_value.value()
   1278                                      << "' for <" << tag
   1279                                      << ">; must be an integer");
   1280     return {};
   1281   }
   1282 
   1283   return Attribute::Symbol{
   1284       Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
   1285       val.data};
   1286 }
   1287 
   1288 bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
   1289   const Source source = source_.WithLine(parser->line_number());
   1290 
   1291   Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   1292   if (!maybe_name) {
   1293     diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
   1294     return false;
   1295   }
   1296 
   1297   Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
   1298   if (!maybe_key) {
   1299     diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
   1300     return false;
   1301   }
   1302 
   1303   ResolvePackage(parser, &maybe_key.value());
   1304   maybe_key.value().SetSource(source);
   1305 
   1306   std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
   1307   if (!value) {
   1308     diag_->Error(DiagMessage(source) << "could not parse style item");
   1309     return false;
   1310   }
   1311 
   1312   style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
   1313   return true;
   1314 }
   1315 
   1316 bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
   1317                                 ParsedResource* out_resource) {
   1318   out_resource->name.type = type;
   1319 
   1320   std::unique_ptr<Style> style = util::make_unique<Style>();
   1321 
   1322   Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
   1323   if (maybe_parent) {
   1324     // If the parent is empty, we don't have a parent, but we also don't infer either.
   1325     if (!maybe_parent.value().empty()) {
   1326       std::string err_str;
   1327       style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
   1328       if (!style->parent) {
   1329         diag_->Error(DiagMessage(out_resource->source) << err_str);
   1330         return false;
   1331       }
   1332 
   1333       // Transform the namespace prefix to the actual package name, and mark the reference as
   1334       // private if appropriate.
   1335       ResolvePackage(parser, &style->parent.value());
   1336     }
   1337 
   1338   } else {
   1339     // No parent was specified, so try inferring it from the style name.
   1340     std::string style_name = out_resource->name.entry;
   1341     size_t pos = style_name.find_last_of(u'.');
   1342     if (pos != std::string::npos) {
   1343       style->parent_inferred = true;
   1344       style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
   1345     }
   1346   }
   1347 
   1348   bool error = false;
   1349   const size_t depth = parser->depth();
   1350   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1351     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1352       // Skip text and comments.
   1353       continue;
   1354     }
   1355 
   1356     const std::string& element_namespace = parser->element_namespace();
   1357     const std::string& element_name = parser->element_name();
   1358     if (element_namespace == "" && element_name == "item") {
   1359       error |= !ParseStyleItem(parser, style.get());
   1360 
   1361     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1362       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1363                    << ":" << element_name << ">");
   1364       error = true;
   1365     }
   1366   }
   1367 
   1368   if (error) {
   1369     return false;
   1370   }
   1371 
   1372   out_resource->value = std::move(style);
   1373   return true;
   1374 }
   1375 
   1376 bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1377   uint32_t resource_format = android::ResTable_map::TYPE_ANY;
   1378   if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
   1379     resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
   1380     if (resource_format == 0u) {
   1381       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1382                    << "'" << format_attr.value() << "' is an invalid format");
   1383       return false;
   1384     }
   1385   }
   1386   return ParseArrayImpl(parser, out_resource, resource_format);
   1387 }
   1388 
   1389 bool ResourceParser::ParseIntegerArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1390   return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_INTEGER);
   1391 }
   1392 
   1393 bool ResourceParser::ParseStringArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   1394   return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_STRING);
   1395 }
   1396 
   1397 bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
   1398                                     ParsedResource* out_resource,
   1399                                     const uint32_t typeMask) {
   1400   out_resource->name.type = ResourceType::kArray;
   1401 
   1402   std::unique_ptr<Array> array = util::make_unique<Array>();
   1403 
   1404   bool translatable = options_.translatable;
   1405   if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
   1406     Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
   1407     if (!maybe_translatable) {
   1408       diag_->Error(DiagMessage(out_resource->source)
   1409                    << "invalid value for 'translatable'. Must be a boolean");
   1410       return false;
   1411     }
   1412     translatable = maybe_translatable.value();
   1413   }
   1414   array->SetTranslatable(translatable);
   1415 
   1416   bool error = false;
   1417   const size_t depth = parser->depth();
   1418   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1419     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1420       // Skip text and comments.
   1421       continue;
   1422     }
   1423 
   1424     const Source item_source = source_.WithLine(parser->line_number());
   1425     const std::string& element_namespace = parser->element_namespace();
   1426     const std::string& element_name = parser->element_name();
   1427     if (element_namespace.empty() && element_name == "item") {
   1428       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
   1429       if (!item) {
   1430         diag_->Error(DiagMessage(item_source) << "could not parse array item");
   1431         error = true;
   1432         continue;
   1433       }
   1434       item->SetSource(item_source);
   1435       array->elements.emplace_back(std::move(item));
   1436 
   1437     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1438       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
   1439                    << "unknown tag <" << element_namespace << ":"
   1440                    << element_name << ">");
   1441       error = true;
   1442     }
   1443   }
   1444 
   1445   if (error) {
   1446     return false;
   1447   }
   1448 
   1449   out_resource->value = std::move(array);
   1450   return true;
   1451 }
   1452 
   1453 bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
   1454                                  ParsedResource* out_resource) {
   1455   out_resource->name.type = ResourceType::kPlurals;
   1456 
   1457   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
   1458 
   1459   bool error = false;
   1460   const size_t depth = parser->depth();
   1461   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1462     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1463       // Skip text and comments.
   1464       continue;
   1465     }
   1466 
   1467     const Source item_source = source_.WithLine(parser->line_number());
   1468     const std::string& element_namespace = parser->element_namespace();
   1469     const std::string& element_name = parser->element_name();
   1470     if (element_namespace.empty() && element_name == "item") {
   1471       Maybe<StringPiece> maybe_quantity =
   1472           xml::FindNonEmptyAttribute(parser, "quantity");
   1473       if (!maybe_quantity) {
   1474         diag_->Error(DiagMessage(item_source)
   1475                      << "<item> in <plurals> requires attribute "
   1476                      << "'quantity'");
   1477         error = true;
   1478         continue;
   1479       }
   1480 
   1481       StringPiece trimmed_quantity =
   1482           util::TrimWhitespace(maybe_quantity.value());
   1483       size_t index = 0;
   1484       if (trimmed_quantity == "zero") {
   1485         index = Plural::Zero;
   1486       } else if (trimmed_quantity == "one") {
   1487         index = Plural::One;
   1488       } else if (trimmed_quantity == "two") {
   1489         index = Plural::Two;
   1490       } else if (trimmed_quantity == "few") {
   1491         index = Plural::Few;
   1492       } else if (trimmed_quantity == "many") {
   1493         index = Plural::Many;
   1494       } else if (trimmed_quantity == "other") {
   1495         index = Plural::Other;
   1496       } else {
   1497         diag_->Error(DiagMessage(item_source)
   1498                      << "<item> in <plural> has invalid value '"
   1499                      << trimmed_quantity << "' for attribute 'quantity'");
   1500         error = true;
   1501         continue;
   1502       }
   1503 
   1504       if (plural->values[index]) {
   1505         diag_->Error(DiagMessage(item_source) << "duplicate quantity '"
   1506                                               << trimmed_quantity << "'");
   1507         error = true;
   1508         continue;
   1509       }
   1510 
   1511       if (!(plural->values[index] = ParseXml(
   1512                 parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
   1513         error = true;
   1514       }
   1515       plural->values[index]->SetSource(item_source);
   1516 
   1517     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1518       diag_->Error(DiagMessage(item_source) << "unknown tag <"
   1519                                             << element_namespace << ":"
   1520                                             << element_name << ">");
   1521       error = true;
   1522     }
   1523   }
   1524 
   1525   if (error) {
   1526     return false;
   1527   }
   1528 
   1529   out_resource->value = std::move(plural);
   1530   return true;
   1531 }
   1532 
   1533 bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
   1534                                            ParsedResource* out_resource) {
   1535   out_resource->name.type = ResourceType::kStyleable;
   1536 
   1537   // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
   1538   out_resource->visibility_level = Visibility::Level::kPublic;
   1539 
   1540   // Declare-styleable only ends up in default config;
   1541   if (out_resource->config != ConfigDescription::DefaultConfig()) {
   1542     diag_->Warn(DiagMessage(out_resource->source)
   1543                 << "ignoring configuration '" << out_resource->config
   1544                 << "' for styleable " << out_resource->name.entry);
   1545     out_resource->config = ConfigDescription::DefaultConfig();
   1546   }
   1547 
   1548   std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
   1549 
   1550   std::string comment;
   1551   bool error = false;
   1552   const size_t depth = parser->depth();
   1553   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
   1554     if (parser->event() == xml::XmlPullParser::Event::kComment) {
   1555       comment = util::TrimWhitespace(parser->comment()).to_string();
   1556       continue;
   1557     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
   1558       // Ignore text.
   1559       continue;
   1560     }
   1561 
   1562     const Source item_source = source_.WithLine(parser->line_number());
   1563     const std::string& element_namespace = parser->element_namespace();
   1564     const std::string& element_name = parser->element_name();
   1565     if (element_namespace.empty() && element_name == "attr") {
   1566       Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
   1567       if (!maybe_name) {
   1568         diag_->Error(DiagMessage(item_source) << "<attr> tag must have a 'name' attribute");
   1569         error = true;
   1570         continue;
   1571       }
   1572 
   1573       // If this is a declaration, the package name may be in the name. Separate
   1574       // these out.
   1575       // Eg. <attr name="android:text" />
   1576       Maybe<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
   1577       if (!maybe_ref) {
   1578         diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
   1579                                               << maybe_name.value() << "'");
   1580         error = true;
   1581         continue;
   1582       }
   1583 
   1584       Reference& child_ref = maybe_ref.value();
   1585       xml::ResolvePackage(parser, &child_ref);
   1586 
   1587       // Create the ParsedResource that will add the attribute to the table.
   1588       ParsedResource child_resource;
   1589       child_resource.name = child_ref.name.value();
   1590       child_resource.source = item_source;
   1591       child_resource.comment = std::move(comment);
   1592 
   1593       if (!ParseAttrImpl(parser, &child_resource, true)) {
   1594         error = true;
   1595         continue;
   1596       }
   1597 
   1598       // Create the reference to this attribute.
   1599       child_ref.SetComment(child_resource.comment);
   1600       child_ref.SetSource(item_source);
   1601       styleable->entries.push_back(std::move(child_ref));
   1602 
   1603       out_resource->child_resources.push_back(std::move(child_resource));
   1604 
   1605     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
   1606       diag_->Error(DiagMessage(item_source) << "unknown tag <"
   1607                                             << element_namespace << ":"
   1608                                             << element_name << ">");
   1609       error = true;
   1610     }
   1611 
   1612     comment = {};
   1613   }
   1614 
   1615   if (error) {
   1616     return false;
   1617   }
   1618 
   1619   out_resource->value = std::move(styleable);
   1620   return true;
   1621 }
   1622 
   1623 }  // namespace aapt
   1624