Loading tools/aapt2/Resource.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,8 @@ StringPiece to_string(ResourceType type) { return "interpolator"; case ResourceType::kLayout: return "layout"; case ResourceType::kMacro: return "macro"; case ResourceType::kMenu: return "menu"; case ResourceType::kMipmap: Loading Loading @@ -119,6 +121,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{ {"integer", ResourceType::kInteger}, {"interpolator", ResourceType::kInterpolator}, {"layout", ResourceType::kLayout}, {"macro", ResourceType::kMacro}, {"menu", ResourceType::kMenu}, {"mipmap", ResourceType::kMipmap}, {"navigation", ResourceType::kNavigation}, Loading tools/aapt2/Resource.h +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ enum class ResourceType { kInteger, kInterpolator, kLayout, kMacro, kMenu, kMipmap, kNavigation, Loading tools/aapt2/ResourceParser.cpp +85 −25 Original line number Diff line number Diff line Loading @@ -627,6 +627,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) { Loading Loading @@ -726,6 +736,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 Loading @@ -733,42 +761,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(NewResourceBuilder(name).SetValue(std::move(id)).Build(), 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; } Loading @@ -777,17 +809,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(); } Loading Loading @@ -850,6 +881,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) Loading tools/aapt2/ResourceParser.h +17 −3 Original line number Diff line number Diff line Loading @@ -57,6 +57,14 @@ struct ResourceParserOptions { Maybe<Visibility::Level> visibility; }; struct FlattenedXmlSubTree { std::string raw_value; StyleString style_string; std::vector<UntranslatableSection> untranslatable_sections; xml::IPackageDeclStack* namespace_resolver; Source source; }; /* * Parses an XML file for resources and adds them to a ResourceTable. */ Loading @@ -67,9 +75,16 @@ class ResourceParser { const ResourceParserOptions& options = {}); bool Parse(xml::XmlPullParser* parser); static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask, bool allow_raw_value, ResourceTable& table, const android::ConfigDescription& config, IDiagnostics& diag); private: DISALLOW_COPY_AND_ASSIGN(ResourceParser); std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser); // Parses the XML subtree as a StyleString (flattened XML representation for strings with // formatting). If parsing fails, false is returned and the out parameters are left in an // unspecified state. Otherwise, Loading @@ -96,7 +111,7 @@ class ResourceParser { bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format); bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource); Loading @@ -108,8 +123,7 @@ class ResourceParser { bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak); Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser, const android::StringPiece& tag); bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStyleItem(xml::XmlPullParser* parser, Style* style); bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource); Loading tools/aapt2/ResourceParser_test.cpp +84 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,90 @@ TEST_F(ResourceParserTest, ParseAttr) { EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY)); } TEST_F(ResourceParserTest, ParseMacro) { std::string input = R"(<macro name="foo">12345</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("12345")); EXPECT_THAT(macro->style_string.str, Eq("12345")); EXPECT_THAT(macro->style_string.spans, IsEmpty()); EXPECT_THAT(macro->untranslatable_sections, IsEmpty()); EXPECT_THAT(macro->alias_namespaces, IsEmpty()); } TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) { std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house.")); EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house.")); EXPECT_THAT(macro->style_string.spans.size(), Eq(1)); EXPECT_THAT(macro->style_string.spans[0].name, Eq("b")); EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12)); EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16)); ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1)); EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12)); EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17)); EXPECT_THAT(macro->alias_namespaces, IsEmpty()); } TEST_F(ResourceParserTest, ParseMacroNamespaces) { std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android"> @app:string/foo</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo")); EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo")); EXPECT_THAT(macro->style_string.spans, IsEmpty()); EXPECT_THAT(macro->untranslatable_sections, IsEmpty()); EXPECT_THAT(macro->alias_namespaces.size(), Eq(1)); EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app")); EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android")); EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false)); } TEST_F(ResourceParserTest, ParseMacroReference) { std::string input = R"(<string name="res_string">@macro/foo</string>)"; ASSERT_TRUE(TestParse(input)); Reference* macro = test::GetValue<Reference>(&table_, "string/res_string"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING)); EXPECT_THAT(macro->allow_raw, Eq(false)); input = R"(<style name="foo"> <item name="bar">@macro/foo</item> </style>)"; ASSERT_TRUE(TestParse(input)); Style* style = test::GetValue<Style>(&table_, "style/foo"); ASSERT_THAT(style, NotNull()); EXPECT_THAT(style->entries.size(), Eq(1)); macro = ValueCast<Reference>(style->entries[0].value.get()); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->type_flags, Eq(0U)); EXPECT_THAT(macro->allow_raw, Eq(true)); } TEST_F(ResourceParserTest, ParseMacroNoNameFail) { std::string input = R"(<macro>12345</macro>)"; ASSERT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) { const ConfigDescription watch_config = test::ParseConfigOrDie("watch"); std::string input = R"(<macro name="foo">12345</macro>)"; ASSERT_FALSE(TestParse(input, watch_config)); } // Old AAPT allowed attributes to be defined under different configurations, but ultimately // stored them with the default configuration. Check that we have the same behavior. TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) { Loading Loading
tools/aapt2/Resource.cpp +3 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,8 @@ StringPiece to_string(ResourceType type) { return "interpolator"; case ResourceType::kLayout: return "layout"; case ResourceType::kMacro: return "macro"; case ResourceType::kMenu: return "menu"; case ResourceType::kMipmap: Loading Loading @@ -119,6 +121,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{ {"integer", ResourceType::kInteger}, {"interpolator", ResourceType::kInterpolator}, {"layout", ResourceType::kLayout}, {"macro", ResourceType::kMacro}, {"menu", ResourceType::kMenu}, {"mipmap", ResourceType::kMipmap}, {"navigation", ResourceType::kNavigation}, Loading
tools/aapt2/Resource.h +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ enum class ResourceType { kInteger, kInterpolator, kLayout, kMacro, kMenu, kMipmap, kNavigation, Loading
tools/aapt2/ResourceParser.cpp +85 −25 Original line number Diff line number Diff line Loading @@ -627,6 +627,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) { Loading Loading @@ -726,6 +736,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 Loading @@ -733,42 +761,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(NewResourceBuilder(name).SetValue(std::move(id)).Build(), 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; } Loading @@ -777,17 +809,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(); } Loading Loading @@ -850,6 +881,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) Loading
tools/aapt2/ResourceParser.h +17 −3 Original line number Diff line number Diff line Loading @@ -57,6 +57,14 @@ struct ResourceParserOptions { Maybe<Visibility::Level> visibility; }; struct FlattenedXmlSubTree { std::string raw_value; StyleString style_string; std::vector<UntranslatableSection> untranslatable_sections; xml::IPackageDeclStack* namespace_resolver; Source source; }; /* * Parses an XML file for resources and adds them to a ResourceTable. */ Loading @@ -67,9 +75,16 @@ class ResourceParser { const ResourceParserOptions& options = {}); bool Parse(xml::XmlPullParser* parser); static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask, bool allow_raw_value, ResourceTable& table, const android::ConfigDescription& config, IDiagnostics& diag); private: DISALLOW_COPY_AND_ASSIGN(ResourceParser); std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser); // Parses the XML subtree as a StyleString (flattened XML representation for strings with // formatting). If parsing fails, false is returned and the out parameters are left in an // unspecified state. Otherwise, Loading @@ -96,7 +111,7 @@ class ResourceParser { bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format); bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource); Loading @@ -108,8 +123,7 @@ class ResourceParser { bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak); Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser, const android::StringPiece& tag); bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseStyleItem(xml::XmlPullParser* parser, Style* style); bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource); Loading
tools/aapt2/ResourceParser_test.cpp +84 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,90 @@ TEST_F(ResourceParserTest, ParseAttr) { EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY)); } TEST_F(ResourceParserTest, ParseMacro) { std::string input = R"(<macro name="foo">12345</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("12345")); EXPECT_THAT(macro->style_string.str, Eq("12345")); EXPECT_THAT(macro->style_string.spans, IsEmpty()); EXPECT_THAT(macro->untranslatable_sections, IsEmpty()); EXPECT_THAT(macro->alias_namespaces, IsEmpty()); } TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) { std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house.")); EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house.")); EXPECT_THAT(macro->style_string.spans.size(), Eq(1)); EXPECT_THAT(macro->style_string.spans[0].name, Eq("b")); EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12)); EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16)); ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1)); EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12)); EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17)); EXPECT_THAT(macro->alias_namespaces, IsEmpty()); } TEST_F(ResourceParserTest, ParseMacroNamespaces) { std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android"> @app:string/foo</macro>)"; ASSERT_TRUE(TestParse(input)); Macro* macro = test::GetValue<Macro>(&table_, "macro/foo"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo")); EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo")); EXPECT_THAT(macro->style_string.spans, IsEmpty()); EXPECT_THAT(macro->untranslatable_sections, IsEmpty()); EXPECT_THAT(macro->alias_namespaces.size(), Eq(1)); EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app")); EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android")); EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false)); } TEST_F(ResourceParserTest, ParseMacroReference) { std::string input = R"(<string name="res_string">@macro/foo</string>)"; ASSERT_TRUE(TestParse(input)); Reference* macro = test::GetValue<Reference>(&table_, "string/res_string"); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING)); EXPECT_THAT(macro->allow_raw, Eq(false)); input = R"(<style name="foo"> <item name="bar">@macro/foo</item> </style>)"; ASSERT_TRUE(TestParse(input)); Style* style = test::GetValue<Style>(&table_, "style/foo"); ASSERT_THAT(style, NotNull()); EXPECT_THAT(style->entries.size(), Eq(1)); macro = ValueCast<Reference>(style->entries[0].value.get()); ASSERT_THAT(macro, NotNull()); EXPECT_THAT(macro->type_flags, Eq(0U)); EXPECT_THAT(macro->allow_raw, Eq(true)); } TEST_F(ResourceParserTest, ParseMacroNoNameFail) { std::string input = R"(<macro>12345</macro>)"; ASSERT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) { const ConfigDescription watch_config = test::ParseConfigOrDie("watch"); std::string input = R"(<macro name="foo">12345</macro>)"; ASSERT_FALSE(TestParse(input, watch_config)); } // Old AAPT allowed attributes to be defined under different configurations, but ultimately // stored them with the default configuration. Check that we have the same behavior. TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) { Loading