Loading tools/aapt2/ResourceParser.cpp +27 −3 Original line number Diff line number Diff line Loading @@ -208,6 +208,15 @@ class SegmentNode : public Node { } }; // A chunk of text in the XML string within a CDATA tags. class CdataSegmentNode : public SegmentNode { public: void Build(StringBuilder* builder) const override { builder->AppendText(data, /* preserve_spaces */ true); } }; // A tag that will be encoded into the final flattened string. Tags like <b> or <i>. class SpanNode : public Node { public: Loading Loading @@ -244,6 +253,7 @@ bool ResourceParser::FlattenXmlSubtree( std::vector<Node*> node_stack; node_stack.push_back(&root); bool cdata_block = false; bool saw_span_node = false; SegmentNode* first_segment = nullptr; SegmentNode* last_segment = nullptr; Loading @@ -253,11 +263,15 @@ bool ResourceParser::FlattenXmlSubtree( const xml::XmlPullParser::Event event = parser->event(); // First take care of any SegmentNodes that should be created. if (event == xml::XmlPullParser::Event::kStartElement || event == xml::XmlPullParser::Event::kEndElement) { if (event == xml::XmlPullParser::Event::kStartElement || event == xml::XmlPullParser::Event::kEndElement || event == xml::XmlPullParser::Event::kCdataStart || event == xml::XmlPullParser::Event::kCdataEnd) { if (!current_text.empty()) { std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>(); std::unique_ptr<SegmentNode> segment_node = (cdata_block) ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>(); segment_node->data = std::move(current_text); last_segment = node_stack.back()->AddChild(std::move(segment_node)); if (first_segment == nullptr) { first_segment = last_segment; Loading Loading @@ -333,6 +347,16 @@ bool ResourceParser::FlattenXmlSubtree( } } break; case xml::XmlPullParser::Event::kCdataStart: { cdata_block = true; break; } case xml::XmlPullParser::Event::kCdataEnd: { cdata_block = false; break; } default: // ignore. break; Loading tools/aapt2/ResourceParser_test.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -989,4 +989,40 @@ TEST_F(ResourceParserTest, ParseIdItem) { ASSERT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseCData) { std::string input = R"( <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)"; ASSERT_TRUE(TestParse(input)); String* output = test::GetValue<String>(&table_, "string/foo"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe")); // Double quotes should not change the state of whitespace processing input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]> World</string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo2"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>' World").data())); // Cdata blocks should not have their whitespace trimmed input = R"(<string name="foo3"> <![CDATA[ text ]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo3"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string(" text ").data())); input = R"(<string name="foo4"> <![CDATA[]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo4"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string("").data())); input = R"(<string name="foo5"> <![CDATA[ ]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo5"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string(" ").data())); } } // namespace aapt tools/aapt2/ResourceUtils.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -797,16 +797,20 @@ StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) { } StringBuilder& StringBuilder::AppendText(const std::string& text) { StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) { if (!error_.empty()) { return *this; } // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed // to preserve spaces preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_; const size_t previous_len = xml_string_.text.size(); Utf8Iterator iter(text); while (iter.HasNext()) { char32_t codepoint = iter.Next(); if (!quote_ && iswspace(codepoint)) { if (!preserve_spaces && !quote_ && iswspace(codepoint)) { if (!last_codepoint_was_space_) { // Emit a space if it's the first. xml_string_.text += ' '; Loading @@ -827,7 +831,6 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) { case U't': xml_string_.text += '\t'; break; case U'n': xml_string_.text += '\n'; break; Loading Loading @@ -855,12 +858,12 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) { break; } } } else if (!preserve_spaces_ && codepoint == U'"') { } else if (!preserve_spaces && codepoint == U'"') { // Only toggle the quote state when we are not preserving spaces. quote_ = !quote_; } else if (!quote_ && codepoint == U'\'') { // This should be escaped. } else if (!preserve_spaces && !quote_ && codepoint == U'\'') { // This should be escaped when we are not preserving spaces error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str()); return *this; Loading tools/aapt2/ResourceUtils.h +4 −2 Original line number Diff line number Diff line Loading @@ -267,8 +267,10 @@ class StringBuilder { // single quotations can be used without escaping them. explicit StringBuilder(bool preserve_spaces = false); // Appends a chunk of text. StringBuilder& AppendText(const std::string& text); // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and // single quotations can be used without escaping them for this append. Otherwise, the // StringBuilder will behave as it was constructed. StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false); // Starts a Span (tag) with the given name. The name is expected to be of the form: // "tag_name;attr1=value;attr2=value;" Loading tools/aapt2/ResourceUtils_test.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,29 @@ TEST(ResourceUtilsTest, StringBuilderUnicodeCodes) { TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) { EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(), Eq("\"")); // Single quotes should be able to be used without escaping them when preserving spaces and the // spaces should not be trimmed EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys ") .AppendText(" 'this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow ") .to_string(), Eq(" hey guys 'this is so cool' wow ")); // Reading a double quote while preserving spaces should not change the quote state EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys ") .AppendText(" \"this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow ") .to_string(), Eq(" hey guys \"this is so cool' wow ")); EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys\" ") .AppendText(" \"this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow \" ") .to_string(), Eq(" hey guys \"this is so cool' wow ")); } } // namespace aapt Loading
tools/aapt2/ResourceParser.cpp +27 −3 Original line number Diff line number Diff line Loading @@ -208,6 +208,15 @@ class SegmentNode : public Node { } }; // A chunk of text in the XML string within a CDATA tags. class CdataSegmentNode : public SegmentNode { public: void Build(StringBuilder* builder) const override { builder->AppendText(data, /* preserve_spaces */ true); } }; // A tag that will be encoded into the final flattened string. Tags like <b> or <i>. class SpanNode : public Node { public: Loading Loading @@ -244,6 +253,7 @@ bool ResourceParser::FlattenXmlSubtree( std::vector<Node*> node_stack; node_stack.push_back(&root); bool cdata_block = false; bool saw_span_node = false; SegmentNode* first_segment = nullptr; SegmentNode* last_segment = nullptr; Loading @@ -253,11 +263,15 @@ bool ResourceParser::FlattenXmlSubtree( const xml::XmlPullParser::Event event = parser->event(); // First take care of any SegmentNodes that should be created. if (event == xml::XmlPullParser::Event::kStartElement || event == xml::XmlPullParser::Event::kEndElement) { if (event == xml::XmlPullParser::Event::kStartElement || event == xml::XmlPullParser::Event::kEndElement || event == xml::XmlPullParser::Event::kCdataStart || event == xml::XmlPullParser::Event::kCdataEnd) { if (!current_text.empty()) { std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>(); std::unique_ptr<SegmentNode> segment_node = (cdata_block) ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>(); segment_node->data = std::move(current_text); last_segment = node_stack.back()->AddChild(std::move(segment_node)); if (first_segment == nullptr) { first_segment = last_segment; Loading Loading @@ -333,6 +347,16 @@ bool ResourceParser::FlattenXmlSubtree( } } break; case xml::XmlPullParser::Event::kCdataStart: { cdata_block = true; break; } case xml::XmlPullParser::Event::kCdataEnd: { cdata_block = false; break; } default: // ignore. break; Loading
tools/aapt2/ResourceParser_test.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -989,4 +989,40 @@ TEST_F(ResourceParserTest, ParseIdItem) { ASSERT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseCData) { std::string input = R"( <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)"; ASSERT_TRUE(TestParse(input)); String* output = test::GetValue<String>(&table_, "string/foo"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe")); // Double quotes should not change the state of whitespace processing input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]> World</string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo2"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>' World").data())); // Cdata blocks should not have their whitespace trimmed input = R"(<string name="foo3"> <![CDATA[ text ]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo3"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string(" text ").data())); input = R"(<string name="foo4"> <![CDATA[]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo4"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string("").data())); input = R"(<string name="foo5"> <![CDATA[ ]]> </string>)"; ASSERT_TRUE(TestParse(input)); output = test::GetValue<String>(&table_, "string/foo5"); ASSERT_THAT(output, NotNull()); EXPECT_THAT(*output, StrValueEq(std::string(" ").data())); } } // namespace aapt
tools/aapt2/ResourceUtils.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -797,16 +797,20 @@ StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) { } StringBuilder& StringBuilder::AppendText(const std::string& text) { StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) { if (!error_.empty()) { return *this; } // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed // to preserve spaces preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_; const size_t previous_len = xml_string_.text.size(); Utf8Iterator iter(text); while (iter.HasNext()) { char32_t codepoint = iter.Next(); if (!quote_ && iswspace(codepoint)) { if (!preserve_spaces && !quote_ && iswspace(codepoint)) { if (!last_codepoint_was_space_) { // Emit a space if it's the first. xml_string_.text += ' '; Loading @@ -827,7 +831,6 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) { case U't': xml_string_.text += '\t'; break; case U'n': xml_string_.text += '\n'; break; Loading Loading @@ -855,12 +858,12 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) { break; } } } else if (!preserve_spaces_ && codepoint == U'"') { } else if (!preserve_spaces && codepoint == U'"') { // Only toggle the quote state when we are not preserving spaces. quote_ = !quote_; } else if (!quote_ && codepoint == U'\'') { // This should be escaped. } else if (!preserve_spaces && !quote_ && codepoint == U'\'') { // This should be escaped when we are not preserving spaces error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str()); return *this; Loading
tools/aapt2/ResourceUtils.h +4 −2 Original line number Diff line number Diff line Loading @@ -267,8 +267,10 @@ class StringBuilder { // single quotations can be used without escaping them. explicit StringBuilder(bool preserve_spaces = false); // Appends a chunk of text. StringBuilder& AppendText(const std::string& text); // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and // single quotations can be used without escaping them for this append. Otherwise, the // StringBuilder will behave as it was constructed. StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false); // Starts a Span (tag) with the given name. The name is expected to be of the form: // "tag_name;attr1=value;attr2=value;" Loading
tools/aapt2/ResourceUtils_test.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,29 @@ TEST(ResourceUtilsTest, StringBuilderUnicodeCodes) { TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) { EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(), Eq("\"")); // Single quotes should be able to be used without escaping them when preserving spaces and the // spaces should not be trimmed EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys ") .AppendText(" 'this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow ") .to_string(), Eq(" hey guys 'this is so cool' wow ")); // Reading a double quote while preserving spaces should not change the quote state EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys ") .AppendText(" \"this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow ") .to_string(), Eq(" hey guys \"this is so cool' wow ")); EXPECT_THAT(ResourceUtils::StringBuilder() .AppendText(" hey guys\" ") .AppendText(" \"this is so cool' ", /* preserve_spaces */ true) .AppendText(" wow \" ") .to_string(), Eq(" hey guys \"this is so cool' wow ")); } } // namespace aapt