Change the readContentsAsString to use the XMLOutputFactory instead of the manual writing.

--
MOS_MIGRATED_REVID=124243184
diff --git a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java
index 9c27f04..9267e97 100644
--- a/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java
+++ b/src/tools/android/java/com/google/devtools/build/android/XmlResourceValues.java
@@ -29,6 +29,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.StringWriter;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -40,19 +41,22 @@
 
 import javax.annotation.Nullable;
 import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventFactory;
 import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.events.Attribute;
 import javax.xml.stream.events.Characters;
 import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.Namespace;
 import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
 
 /**
  * {@link XmlResourceValues} provides methods for getting {@link XmlResourceValue} derived classes.
  *
- * <p>
- * Acts a static factory class containing the general xml parsing logic for resources that are
+ * <p>Acts a static factory class containing the general xml parsing logic for resources that are
  * declared inside the &lt;resources&gt; tag.
  */
 public class XmlResourceValues {
@@ -74,6 +78,9 @@
   private static final QName ATTR_PARENT = QName.valueOf("parent");
   private static final QName ATTR_TYPE = QName.valueOf("type");
 
+  private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
+  private static final XMLEventFactory XML_EVENT_FACTORY = XMLEventFactory.newInstance();
+
   static final String XLIFF_NAMESPACE = "urn:oasis:names:tc:xliff:document:1.2";
   private static final String XLIFF_PREFIX = "xliff";
 
@@ -160,11 +167,7 @@
       throws XMLStreamException {
     // Using a map to deduplicate xmlns declarations on the attributes.
     Map<String, String> attributeMap = new LinkedHashMap<>();
-    @SuppressWarnings({
-      "cast",
-      "unchecked"
-    }) // The interface returns Iterator, force casting based on documentation.
-    Iterator<Attribute> attributes = (Iterator<Attribute>) start.getAttributes();
+    Iterator<Attribute> attributes = iterateAttributesFrom(start);
     while (attributes.hasNext()) {
       Attribute attribute = attributes.next();
       QName name = attribute.getName();
@@ -218,40 +221,31 @@
   @Nullable
   public static String readContentsAsString(XMLEventReader eventReader, QName startTag)
       throws XMLStreamException {
-    StringBuilder contents = new StringBuilder();
+    StringWriter contents = new StringWriter();
+    XMLEventWriter writer = XML_OUTPUT_FACTORY.createXMLEventWriter(contents);
     while (!isEndTag(eventReader.peek(), startTag)) {
       XMLEvent xmlEvent = eventReader.nextEvent();
-      if (xmlEvent.isCharacters()) {
-        contents.append(escapeXmlValues(xmlEvent.asCharacters().getData()));
-      } else if (xmlEvent.isStartElement()) {
+      if (xmlEvent.isStartElement()) {
         // TODO(corysmith): Replace this with a proper representation of the contents that can be
         // serialized and reconstructed appropriately without modification.
-        QName name = xmlEvent.asStartElement().getName();
-        contents.append("<");
-        if (!name.getNamespaceURI().isEmpty()) {
-          contents
-              .append(name.getPrefix())
-              .append(':')
-              .append(name.getLocalPart())
-              .append(' ')
-              .append("xmlns:")
-              .append(name.getPrefix())
-              .append("='")
-              .append(name.getNamespaceURI())
-              .append("'");
-        } else {
-          contents.append(name.getLocalPart());
+        StartElement start = xmlEvent.asStartElement();
+        QName name = start.getName();
+        // Lazy with the list for memory usage -- this code path happens a lot.
+        List<Namespace> namespaces = maybeAddNamespace(name, null);
+        Iterator<Attribute> attributes = iterateAttributesFrom(start);
+        while (attributes.hasNext()) {
+          Attribute attribute = attributes.next();
+          namespaces = maybeAddNamespace(attribute.getName(), namespaces);
         }
-        contents.append(">");
-      } else if (xmlEvent.isEndElement()) {
-        QName name = xmlEvent.asEndElement().getName();
-        contents.append("</");
-        if (!name.getNamespaceURI().isEmpty()) {
-          contents.append(name.getPrefix()).append(':').append(name.getLocalPart());
+        if (namespaces != null) {
+          writer.add(
+              XML_EVENT_FACTORY.createStartElement(
+                  name, iterateAttributesFrom(start), namespaces.iterator()));
         } else {
-          contents.append(name.getLocalPart());
+          writer.add(xmlEvent);
         }
-        contents.append(">");
+      } else {
+        writer.add(xmlEvent);
       }
     }
     // Verify the end element.
@@ -260,6 +254,28 @@
     return contents.toString();
   }
 
+  @SuppressWarnings({
+    "cast",
+    "unchecked"
+  }) // The interface returns Iterator, force casting based on documentation.
+  private static Iterator<Attribute> iterateAttributesFrom(StartElement start) {
+    return (Iterator<Attribute>) start.getAttributes();
+  }
+
+  private static List<Namespace> maybeAddNamespace(QName name, List<Namespace> namespaces) {
+    // The xliff namespace is provided by default.
+    if (name.getNamespaceURI() != null
+        && !name.getNamespaceURI().isEmpty()
+        && !(XLIFF_NAMESPACE.equals(name.getNamespaceURI())
+            && XLIFF_PREFIX.equals(name.getPrefix()))) {
+      if (namespaces == null) {
+        namespaces = new ArrayList<>();
+      }
+      namespaces.add(XML_EVENT_FACTORY.createNamespace(name.getPrefix(), name.getNamespaceURI()));
+    }
+    return namespaces;
+  }
+
   /* XML helper methods follow. */
   // TODO(corysmith): Move these to a wrapper class for XMLEventReader.
   private static String parseReferenceFromElementAttribute(
@@ -337,8 +353,7 @@
         logger.fine(
             String.format(
                 "Invalid characters [%s] found at %s",
-                characters.getData(),
-                characters.getLocation().getLineNumber()));
+                characters.getData(), characters.getLocation().getLineNumber()));
       }
     }
     return eventReader.nextEvent();