diff options
Diffstat (limited to 'tools/aapt2/ResourceParser.cpp')
-rw-r--r-- | tools/aapt2/ResourceParser.cpp | 264 |
1 files changed, 178 insertions, 86 deletions
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 931a14b1f650..f1e2da9f41e2 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -42,6 +42,11 @@ using ::android::StringPiece; using android::idmap2::policy::kPolicyStringToFlag; namespace aapt { +namespace { +constexpr const char* kPublicGroupTag = "public-group"; +constexpr const char* kStagingPublicGroupTag = "staging-public-group"; +constexpr const char* kStagingPublicGroupFinalTag = "staging-public-group-final"; +} // namespace constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2"; @@ -102,8 +107,10 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; + bool staged_api = false; bool allow_new = false; Maybe<OverlayableItem> overlayable_item; + Maybe<StagedId> staged_alias; std::string comment; std::unique_ptr<Value> value; @@ -118,43 +125,48 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed res->comment = trimmed_comment.to_string(); } + NewResourceBuilder res_builder(res->name); if (res->visibility_level != Visibility::Level::kUndefined) { Visibility visibility; visibility.level = res->visibility_level; + visibility.staged_api = res->staged_api; visibility.source = res->source; visibility.comment = res->comment; - if (!table->SetVisibilityWithId(res->name, visibility, res->id, diag)) { - return false; - } + res_builder.SetVisibility(visibility); + } + + if (res->id.is_valid()) { + res_builder.SetId(res->id); } if (res->allow_new) { AllowNew allow_new; allow_new.source = res->source; allow_new.comment = res->comment; - if (!table->SetAllowNew(res->name, allow_new, diag)) { - return false; - } + res_builder.SetAllowNew(allow_new); } if (res->overlayable_item) { - if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) { - return false; - } + res_builder.SetOverlayable(res->overlayable_item.value()); } if (res->value != nullptr) { // Attach the comment, source and config to the value. res->value->SetComment(std::move(res->comment)); res->value->SetSource(std::move(res->source)); + res_builder.SetValue(std::move(res->value), res->config, res->product); + } - if (!table->AddResourceWithId(res->name, res->id, res->config, res->product, - std::move(res->value), diag)) { - return false; - } + if (res->staged_alias) { + res_builder.SetStagedId(res->staged_alias.value()); } bool error = false; + if (!res->name.entry.empty()) { + if (!table->AddResource(res_builder.Build(), diag)) { + return false; + } + } for (ParsedResource& child : res->child_resources) { error |= !AddResourcesToTable(table, diag, &child); } @@ -342,7 +354,7 @@ bool ResourceParser::FlattenXmlSubtree( } } - // Sanity check to make sure we processed all the nodes. + // Validity check to make sure we processed all the nodes. CHECK(node_stack.size() == 1u); CHECK(node_stack.back() == &root); @@ -525,6 +537,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, {"plurals", std::mem_fn(&ResourceParser::ParsePlural)}, {"public", std::mem_fn(&ResourceParser::ParsePublic)}, {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)}, + {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)}, + {"staging-public-group-final", std::mem_fn(&ResourceParser::ParseStagingPublicGroupFinal)}, {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)}, {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle, std::placeholders::_2, std::placeholders::_3)}, @@ -620,6 +634,16 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, } return true; + } else if (resource_type == "macro") { + if (!maybe_name) { + diag_->Error(DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> missing 'name' attribute"); + return false; + } + + out_resource->name.type = ResourceType::kMacro; + out_resource->name.entry = maybe_name.value().to_string(); + return ParseMacro(parser, out_resource); } if (can_be_item) { @@ -653,7 +677,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, const auto bag_iter = elToBagMap.find(resource_type); if (bag_iter != elToBagMap.end()) { // Ensure we have a name (unless this is a <public-group> or <overlayable>). - if (resource_type != "public-group" && resource_type != "overlayable") { + if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag && + resource_type != kStagingPublicGroupFinalTag && resource_type != "overlayable") { if (!maybe_name) { diag_->Error(DiagMessage(out_resource->source) << "<" << parser->element_name() << "> missing 'name' attribute"); @@ -718,6 +743,24 @@ bool ResourceParser::ParseItem(xml::XmlPullParser* parser, return true; } +std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree( + xml::XmlPullParser* parser) { + const size_t begin_xml_line = parser->line_number(); + + std::string raw_value; + StyleString style_string; + std::vector<UntranslatableSection> untranslatable_sections; + if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) { + return {}; + } + + return FlattenedXmlSubTree{.raw_value = raw_value, + .style_string = style_string, + .untranslatable_sections = untranslatable_sections, + .namespace_resolver = parser, + .source = source_.WithLine(begin_xml_line)}; +} + /** * Reads the entire XML subtree and attempts to parse it as some Item, * with typeMask denoting which items it can be. If allowRawValue is @@ -725,42 +768,46 @@ bool ResourceParser::ParseItem(xml::XmlPullParser* parser, * an Item. If allowRawValue is false, nullptr is returned in this * case. */ -std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, - const uint32_t type_mask, +std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask, const bool allow_raw_value) { - const size_t begin_xml_line = parser->line_number(); - - std::string raw_value; - StyleString style_string; - std::vector<UntranslatableSection> untranslatable_sections; - if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) { + auto sub_tree = CreateFlattenSubTree(parser); + if (!sub_tree.has_value()) { return {}; } + return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_); +} - if (!style_string.spans.empty()) { +std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree, + const uint32_t type_mask, const bool allow_raw_value, + ResourceTable& table, + const android::ConfigDescription& config, + IDiagnostics& diag) { + if (!xmlsub_tree.style_string.spans.empty()) { // This can only be a StyledString. std::unique_ptr<StyledString> styled_string = - util::make_unique<StyledString>(table_->string_pool.MakeRef( - style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_))); - styled_string->untranslatable_sections = std::move(untranslatable_sections); + util::make_unique<StyledString>(table.string_pool.MakeRef( + xmlsub_tree.style_string, + StringPool::Context(StringPool::Context::kNormalPriority, config))); + styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections; return std::move(styled_string); } auto on_create_reference = [&](const ResourceName& name) { // name.package can be empty here, as it will assume the package name of the // table. - std::unique_ptr<Id> id = util::make_unique<Id>(); - id->SetSource(source_.WithLine(begin_xml_line)); - table_->AddResource(name, {}, {}, std::move(id), diag_); + auto id = util::make_unique<Id>(); + id->SetSource(xmlsub_tree.source); + return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag); }; // Process the raw value. - std::unique_ptr<Item> processed_item = - ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference); + std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute( + xmlsub_tree.raw_value, type_mask, on_create_reference); if (processed_item) { // Fix up the reference. - if (Reference* ref = ValueCast<Reference>(processed_item.get())) { - ResolvePackage(parser, ref); + if (auto ref = ValueCast<Reference>(processed_item.get())) { + ref->allow_raw = allow_raw_value; + ResolvePackage(xmlsub_tree.namespace_resolver, ref); } return processed_item; } @@ -769,17 +816,16 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, if (type_mask & android::ResTable_map::TYPE_STRING) { // Use the trimmed, escaped string. std::unique_ptr<String> string = util::make_unique<String>( - table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_))); - string->untranslatable_sections = std::move(untranslatable_sections); + table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config))); + string->untranslatable_sections = xmlsub_tree.untranslatable_sections; return std::move(string); } if (allow_raw_value) { // We can't parse this so return a RawString if we are allowed. - return util::make_unique<RawString>( - table_->string_pool.MakeRef(util::TrimWhitespace(raw_value), - StringPool::Context(config_))); - } else if (util::TrimWhitespace(raw_value).empty()) { + return util::make_unique<RawString>(table.string_pool.MakeRef( + util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config))); + } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) { // If the text is empty, and the value is not allowed to be a string, encode it as a @null. return ResourceUtils::MakeNull(); } @@ -842,6 +888,35 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, return true; } +bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) { + auto sub_tree = CreateFlattenSubTree(parser); + if (!sub_tree) { + return false; + } + + if (out_resource->config != ConfigDescription::DefaultConfig()) { + diag_->Error(DiagMessage(out_resource->source) + << "<macro> tags cannot be declared in configurations other than the default " + "configuration'"); + return false; + } + + auto macro = std::make_unique<Macro>(); + macro->raw_value = std::move(sub_tree->raw_value); + macro->style_string = std::move(sub_tree->style_string); + macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections); + + for (const auto& decl : parser->package_decls()) { + macro->alias_namespaces.emplace_back( + Macro::Namespace{.alias = decl.prefix, + .package_name = decl.package.package, + .is_private = decl.package.private_namespace}); + } + + out_resource->value = std::move(macro); + return true; +} + bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (options_.visibility) { diag_->Error(DiagMessage(out_resource->source) @@ -890,54 +965,45 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out return true; } -bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) { - if (options_.visibility) { - diag_->Error(DiagMessage(out_resource->source) - << "<public-group> tag not allowed with --visibility flag"); - return false; - } - +template <typename Func> +bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, + const char* tag_name, IDiagnostics* diag, Func&& func) { if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) - << "ignoring configuration '" << out_resource->config - << "' for <public-group> tag"); + diag->Warn(DiagMessage(out_resource->source) + << "ignoring configuration '" << out_resource->config << "' for <" << tag_name + << "> tag"); } Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { - diag_->Error(DiagMessage(out_resource->source) - << "<public-group> must have a 'type' attribute"); + diag->Error(DiagMessage(out_resource->source) + << "<" << tag_name << "> must have a 'type' attribute"); return false; } const ResourceType* parsed_type = ParseResourceType(maybe_type.value()); if (!parsed_type) { - diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '" - << maybe_type.value() - << "' in <public-group>"); + diag->Error(DiagMessage(out_resource->source) + << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">"); return false; } - Maybe<StringPiece> maybe_id_str = - xml::FindNonEmptyAttribute(parser, "first-id"); + Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id"); if (!maybe_id_str) { - diag_->Error(DiagMessage(out_resource->source) - << "<public-group> must have a 'first-id' attribute"); + diag->Error(DiagMessage(out_resource->source) + << "<" << tag_name << "> must have a 'first-id' attribute"); return false; } - Maybe<ResourceId> maybe_id = - ResourceUtils::ParseResourceId(maybe_id_str.value()); + Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value()); if (!maybe_id) { - diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '" - << maybe_id_str.value() - << "' in <public-group>"); + diag->Error(DiagMessage(out_resource->source) + << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">"); return false; } - ResourceId next_id = maybe_id.value(); - std::string comment; + ResourceId next_id = maybe_id.value(); bool error = false; const size_t depth = parser->depth(); while (xml::XmlPullParser::NextChildNode(parser, depth)) { @@ -949,53 +1015,79 @@ bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource continue; } - const Source item_source = source_.WithLine(parser->line_number()); + const Source item_source = out_resource->source.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "public") { - Maybe<StringPiece> maybe_name = - xml::FindNonEmptyAttribute(parser, "name"); + auto maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { - diag_->Error(DiagMessage(item_source) - << "<public> must have a 'name' attribute"); + diag->Error(DiagMessage(item_source) << "<public> must have a 'name' attribute"); error = true; continue; } if (xml::FindNonEmptyAttribute(parser, "id")) { - diag_->Error(DiagMessage(item_source) - << "'id' is ignored within <public-group>"); + diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">"); error = true; continue; } if (xml::FindNonEmptyAttribute(parser, "type")) { - diag_->Error(DiagMessage(item_source) - << "'type' is ignored within <public-group>"); + diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">"); error = true; continue; } - ParsedResource child_resource; - child_resource.name.type = *parsed_type; - child_resource.name.entry = maybe_name.value().to_string(); - child_resource.id = next_id; - // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment - child_resource.comment = std::move(comment); - child_resource.source = item_source; - child_resource.visibility_level = Visibility::Level::kPublic; - out_resource->child_resources.push_back(std::move(child_resource)); + ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{ + .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()}, + .source = item_source, + .comment = std::move(comment), + }); - next_id.id += 1; + // Execute group specific code. + func(entry_res, next_id); + next_id.id++; } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(item_source) << ":" << element_name << ">"); + diag->Error(DiagMessage(item_source) << ":" << element_name << ">"); error = true; } } return !error; } +bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser, + ParsedResource* out_resource) { + return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_, + [](ParsedResource& parsed_entry, ResourceId id) { + parsed_entry.id = id; + parsed_entry.staged_api = true; + parsed_entry.visibility_level = Visibility::Level::kPublic; + }); +} + +bool ResourceParser::ParseStagingPublicGroupFinal(xml::XmlPullParser* parser, + ParsedResource* out_resource) { + return ParseGroupImpl(parser, out_resource, kStagingPublicGroupFinalTag, diag_, + [](ParsedResource& parsed_entry, ResourceId id) { + parsed_entry.staged_alias = StagedId{id, parsed_entry.source}; + }); +} + +bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) { + if (options_.visibility) { + diag_->Error(DiagMessage(out_resource->source) + << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag"); + return false; + } + + return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_, + [](ParsedResource& parsed_entry, ResourceId id) { + parsed_entry.id = id; + parsed_entry.visibility_level = Visibility::Level::kPublic; + }); +} + bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource) { Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); |