Merge "AAPT2: Fix quoted text in res/xml assets" into pi-dev
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index d897941..2fe2424 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -79,23 +79,31 @@
}
void Visit(const xml::Text* node) override {
- if (util::TrimWhitespace(node->text).empty()) {
- // Skip whitespace only text nodes.
+ std::string text = util::TrimWhitespace(node->text).to_string();
+
+ // Skip whitespace only text nodes.
+ if (text.empty()) {
return;
}
+ // Compact leading and trailing whitespace into a single space
+ if (isspace(node->text[0])) {
+ text = ' ' + text;
+ }
+ if (isspace(node->text[node->text.length() - 1])) {
+ text = text + ' ';
+ }
+
ChunkWriter writer(buffer_);
ResXMLTree_node* flat_node = writer.StartChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
flat_node->lineNumber = util::HostToDevice32(node->line_number);
flat_node->comment.index = util::HostToDevice32(-1);
- ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
-
// Process plain strings to make sure they get properly escaped.
- StringBuilder builder;
- builder.AppendText(node->text);
- AddString(builder.to_string(), kLowPriority, &flat_text->data);
+ text = StringBuilder(true /*preserve_spaces*/).AppendText(text).to_string();
+ ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
+ AddString(text, kLowPriority, &flat_text->data);
writer.Finish();
}
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index 08243fe..25786b1 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -286,6 +286,165 @@
EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}"));
}
+TEST_F(XmlFlattenerTest, ProcessQuotes) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
+ R"(<root>
+ <item>Regular text</item>
+ <item>"Text in double quotes"</item>
+ <item>'Text in single quotes'</item>
+ <item>Text containing "double quotes"</item>
+ <item>Text containing 'single quotes'</item>
+ </root>)");
+
+ size_t len;
+ android::ResXMLTree tree;
+
+ XmlFlattenerOptions options;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"Regular text"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"\"Text in double quotes\""));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"'Text in single quotes'"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing \"double quotes\""));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"Text containing 'single quotes'"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
+}
+
+TEST_F(XmlFlattenerTest, ProcessWhitepspace) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
+ R"(<root>
+ <item> Compact Spaces </item>
+ <item>
+ A
+ </item>
+ <item>B </item>
+ <item>C </item>
+ <item> D </item>
+ <item> E</item>
+ <item> F</item>
+ <item> G </item>
+ <item> H </item>
+<item>
+I
+</item>
+<item>
+
+ J
+
+</item>
+ <item>
+ </item>
+ </root>)");
+
+ size_t len;
+ android::ResXMLTree tree;
+
+ XmlFlattenerOptions options;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" Compact Spaces "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" A "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"B "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"C "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" D "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" E"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" F"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" G "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" H "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" I "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u" J "));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"item"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
+}
+
TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");