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