Merge "SdkMan2: Fix edge case when install/delete packages."
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
index a228fd2..661621d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
@@ -287,13 +287,20 @@
     /**
      * Guess what style to use to edit the given document - layout, resource, manifest, ... ? */
     private XmlFormatStyle guessStyle(IStructuredModel model, Document domDocument) {
+        // The "layout" style is used for most XML resource file types:
+        // layouts, color-lists and state-lists, animations, drawables, menus, etc
         XmlFormatStyle style = XmlFormatStyle.LAYOUT;
+
+        // The "resource" style is used for most value-based XML files:
+        // strings, dimensions, booleans, colors, integers, plurals,
+        // integer-arrays, string-arrays, and typed-arrays
         if (domDocument.getDocumentElement() != null
                 && ResourcesDescriptors.ROOT_ELEMENT.equals(domDocument.getDocumentElement()
                         .getTagName())) {
             style = XmlFormatStyle.RESOURCE;
         }
 
+        // The "manifest" style is used for manifest files
         String baseLocation = model.getBaseLocation();
         if (baseLocation != null) {
             if (baseLocation.endsWith(SdkConstants.FN_ANDROID_MANIFEST_XML)) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java
index 9b97dde..018d976 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinter.java
@@ -15,6 +15,7 @@
  */
 package com.android.ide.eclipse.adt.internal.editors.formatting;
 
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
 import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.COLOR_ELEMENT;
 import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.DIMEN_ELEMENT;
 import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.ITEM_TAG;
@@ -64,11 +65,15 @@
      *
      * @param prefs the preferences to format with
      * @param style the style to format with
-     * @param lineSeparator the line separator to use, such as "\n"
+     * @param lineSeparator the line separator to use, such as "\n" (can be null, in which
+     *     case the system default is looked up via the line.separator property)
      */
     XmlPrettyPrinter(XmlFormatPreferences prefs, XmlFormatStyle style, String lineSeparator) {
         mPrefs = prefs;
         mStyle = style;
+        if (lineSeparator == null) {
+            lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$
+        }
         mLineSeparator = lineSeparator;
     }
 
@@ -85,6 +90,13 @@
      */
     public void prettyPrint(int rootDepth, Node root, Node startNode, Node endNode,
             StringBuilder out) {
+        if (startNode == null) {
+            startNode = root;
+        }
+        if (endNode == null) {
+            endNode = root;
+        }
+
         mStartNode = startNode;
         mEndNode = endNode;
         mOut = out;
@@ -223,10 +235,42 @@
     }
 
     private void printComment(int depth, Node node) {
+        String comment = node.getNodeValue();
+        boolean multiLine = comment.indexOf('\n') != -1;
+        String trimmed = comment.trim();
+
+        // See if this is an "end-of-the-line" comment, e.g. it is not a multi-line
+        // comment and it appears on the same line as an opening or closing element tag;
+        // if so, continue to place it as a suffix comment
+        boolean isSuffixComment = false;
+        if (!multiLine) {
+            Node previous = node.getPreviousSibling();
+            isSuffixComment = true;
+            while (previous != null) {
+                short type = previous.getNodeType();
+                if (type == Node.TEXT_NODE || type == Node.COMMENT_NODE) {
+                    if (previous.getNodeValue().indexOf('\n') != -1) {
+                        isSuffixComment = false;
+                        break;
+                    }
+                } else {
+                    break;
+                }
+                previous = previous.getPreviousSibling();
+            }
+            if (isSuffixComment) {
+                // Remove newline added by element open tag or element close tag
+                if (endsWithLineSeparator()) {
+                    removeLastLineSeparator();
+                }
+                mOut.append(' ');
+            }
+        }
+
         // Put the comment on a line on its own? Only if it does not follow some other comment
         // (e.g. is the first child in an element or follows some other element only separated
         // by whitespace)
-        if (depth > 0) {
+        if (!mPrefs.removeEmptyLines && depth > 0 && !isSuffixComment) {
             Node curr = node.getPreviousSibling();
             if (curr == null
                     || curr.getNodeType() == Node.ELEMENT_NODE
@@ -239,11 +283,10 @@
         }
 
         // TODO: Reformat the comment text?
-        String comment = node.getNodeValue();
-        boolean multiLine = comment.indexOf('\n') != -1;
-        String trimmed = comment.trim();
         if (!multiLine && trimmed.length() < 70) {
-            indent(depth);
+            if (!isSuffixComment) {
+                indent(depth);
+            }
             mOut.append("<!-- ");  //$NON-NLS-1$
             mOut.append(trimmed);
             mOut.append(" -->"); //$NON-NLS-1$
@@ -289,6 +332,45 @@
             mOut.append("-->"); //$NON-NLS-1$
             mOut.append(mLineSeparator);
         }
+
+        // Preserve whitespace after comment: See if the original document had two or
+        // more newlines after the comment, and if so have a blank line between this
+        // comment and the next
+        Node next = node.getNextSibling();
+        if (!mPrefs.removeEmptyLines && next != null && next.getNodeType() == Node.TEXT_NODE) {
+            String text = next.getNodeValue();
+            int newLinesBeforeText = 0;
+            for (int i = 0, n = text.length(); i < n; i++) {
+                char c = text.charAt(i);
+                if (c == '\n') {
+                    newLinesBeforeText++;
+                    if (newLinesBeforeText == 2) {
+                        // Yes
+                        mOut.append(mLineSeparator);
+                        break;
+                    }
+                } else if (!Character.isWhitespace(c)) {
+                    break;
+                }
+            }
+        }
+    }
+
+    private boolean endsWithLineSeparator() {
+        int separatorLength = mLineSeparator.length();
+        if (mOut.length() >= separatorLength) {
+            for (int i = 0, j = mOut.length() - separatorLength; i < separatorLength; i++) {
+               if (mOut.charAt(j) != mLineSeparator.charAt(i)) {
+                   return false;
+               }
+            }
+        }
+
+        return true;
+    }
+
+    private void removeLastLineSeparator() {
+        mOut.setLength(mOut.length() - mLineSeparator.length());
     }
 
     private void printOpenElementTag(int depth, Node node) {
@@ -304,17 +386,6 @@
         NamedNodeMap attributes = element.getAttributes();
         int attributeCount = attributes.getLength();
         if (attributeCount > 0) {
-            // Put the single attribute on the same line as the element tag?
-            boolean singleLine = mPrefs.oneAttributeOnFirstLine && attributeCount == 1
-                    // In resource files we always put all the attributes (which is
-                    // usually just zero, one or two) on the same line
-                    || mStyle == XmlFormatStyle.RESOURCE;
-            if (singleLine) {
-                mOut.append(' ');
-            } else {
-                mOut.append(mLineSeparator);
-            }
-
             // Sort the attributes
             List<Attr> attributeList = new ArrayList<Attr>();
             for (int i = 0, n = attributeCount; i < n; i++) {
@@ -323,9 +394,27 @@
             Comparator<Attr> comparator = mPrefs.sortAttributes.getAttributeComparator();
             Collections.sort(attributeList, comparator);
 
+            // Put the single attribute on the same line as the element tag?
+            boolean singleLine = mPrefs.oneAttributeOnFirstLine && attributeCount == 1
+                    // In resource files we always put all the attributes (which is
+                    // usually just zero, one or two) on the same line
+                    || mStyle == XmlFormatStyle.RESOURCE;
+
+            // We also place the namespace declaration on the same line as the root element,
+            // but this doesn't also imply singleLine handling; subsequent attributes end up
+            // on their own lines
+            boolean indentNextAttribute;
+            if (singleLine || (depth == 0 && XMLNS.equals(attributeList.get(0).getPrefix()))) {
+                mOut.append(' ');
+                indentNextAttribute = false;
+            } else {
+                mOut.append(mLineSeparator);
+                indentNextAttribute = true;
+            }
+
             Attr last = attributeList.get(attributeCount - 1);
             for (Attr attribute : attributeList) {
-                if (!singleLine) {
+                if (indentNextAttribute) {
                     indent(depth + 1);
                 }
                 mOut.append(attribute.getName());
@@ -337,6 +426,7 @@
                 // immediately follow the last attribute
                 if (attribute != last) {
                     mOut.append(singleLine ? " " : mLineSeparator); //$NON-NLS-1$
+                    indentNextAttribute = true;
                 }
             }
         }
@@ -345,7 +435,9 @@
 
         // Add a space before the > or /> ? In resource files, only do this when closing the
         // element
-        if (mPrefs.spaceBeforeClose && (mStyle != XmlFormatStyle.RESOURCE || isClosed)) {
+        if (mPrefs.spaceBeforeClose && (mStyle != XmlFormatStyle.RESOURCE || isClosed)
+                // in <selector> files etc still treat the <item> entries as in resource files
+                && !ITEM_TAG.equals(element.getTagName())) {
             mOut.append(' ');
         }
 
@@ -472,7 +564,7 @@
     }
 
     private boolean newlineBeforeElementClose(Element element, int depth) {
-        return depth == 0;
+        return depth == 0 && !mPrefs.removeEmptyLines;
     }
 
     private boolean newlineAfterElementClose(Element element, int depth) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
index 4f1fd5b..435a101 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AdtPrefs.java
@@ -311,7 +311,10 @@
     @Override
     public void initializeDefaultPreferences() {
         IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+        initializeStoreWithDefaults(store);
+    }
 
+    public void initializeStoreWithDefaults(IPreferenceStore store) {
         store.setDefault(PREFS_BUILD_RES_AUTO_REFRESH, true);
         store.setDefault(PREFS_BUILD_FORCE_ERROR_ON_NATIVELIB_IN_JAR, true);
         store.setDefault(PREFS_BUILD_SKIP_POST_COMPILE_ON_FILE_SAVE, false);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
index 3b11a22..63ed876 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
@@ -25,6 +25,7 @@
 import java.util.Comparator;
 
 /** Order to use when sorting attributes */
+@SuppressWarnings("restriction") // IndexedRegion
 public enum AttributeSortOrder {
     NO_SORTING("none"),     //$NON-NLS-1$
     ALPHABETICAL("alpha"),  //$NON-NLS-1$
@@ -71,7 +72,6 @@
      * Comparator which can be used to "sort" attributes into their existing source order
      * (which is not the same as the node map iteration order in the DOM model)
      */
-    @SuppressWarnings("restriction")
     private static final Comparator<Attr> EXISTING_ORDER_COMPARATOR = new Comparator<Attr>() {
         public int compare(Attr attr1, Attr attr2) {
             IndexedRegion region1 = (IndexedRegion) attr1;
@@ -82,10 +82,21 @@
     };
 
     /**
-     * Comparator which can be used to sort attributes into alphabetical order
+     * Comparator which can be used to sort attributes into alphabetical order (but xmlns
+     * is always first)
      */
     private static final Comparator<Attr> ALPHABETICAL_COMPARATOR = new Comparator<Attr>() {
         public int compare(Attr attr1, Attr attr2) {
+            // Namespace declarations should always go first
+            if (XMLNS.equals(attr1.getPrefix())) {
+                if (XMLNS.equals(attr2.getPrefix())) {
+                    return 0;
+                }
+                return -1;
+            } else if (XMLNS.equals(attr2.getPrefix())) {
+                return 1;
+            }
+
             return attr1.getLocalName().compareTo(attr2.getLocalName());
         }
     };
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java
index ab4cc2f..edccbf0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/XmlPrettyPrinterTest.java
@@ -15,7 +15,12 @@
  */
 package com.android.ide.eclipse.adt.internal.editors.formatting;
 
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+
+import org.eclipse.jface.preference.PreferenceStore;
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -29,8 +34,21 @@
 import junit.framework.TestCase;
 
 public class XmlPrettyPrinterTest extends TestCase {
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        PreferenceStore store = new PreferenceStore();
+        AdtPrefs.init(store);
+        AdtPrefs prefs = AdtPrefs.getPrefs();
+        prefs.initializeStoreWithDefaults(store);
+        prefs.loadValues(null);
+        XmlFormatPreferences formatPrefs = XmlFormatPreferences.create();
+        assertTrue(formatPrefs.oneAttributeOnFirstLine);
+    }
+
     private void checkFormat(XmlFormatPreferences prefs, XmlFormatStyle style,
-            String xml, String expected, String delimiter) throws Exception {
+            String xml, String expected, String delimiter,
+            String startNodeName, String endNodeName) throws Exception {
 
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         InputSource is = new InputSource(new StringReader(xml));
@@ -53,7 +71,16 @@
         XmlPrettyPrinter printer = new XmlPrettyPrinter(prefs, style, delimiter);
 
         StringBuilder sb = new StringBuilder(1000);
-        printer.prettyPrint(-1, document, document, document, sb);
+        Node startNode = document;
+        Node endNode = document;
+        if (startNodeName != null) {
+            startNode = findNode(document.getDocumentElement(), startNodeName);
+        }
+        if (endNodeName != null) {
+            endNode = findNode(document.getDocumentElement(), endNodeName);
+        }
+
+        printer.prettyPrint(-1, document, startNode, endNode, sb);
         String formatted = sb.toString();
         if (!expected.equals(formatted)) {
             System.out.println(formatted);
@@ -61,6 +88,40 @@
         assertEquals(expected, formatted);
     }
 
+    private Node findNode(Node node, String nodeName) {
+        if (node.getNodeName().equals(nodeName)) {
+            return node;
+        }
+
+        NodeList children = node.getChildNodes();
+        for (int i = 0, n = children.getLength(); i < n; i++) {
+            Node child = children.item(i);
+            Node result = findNode(child, nodeName);
+            if (result != null) {
+                return result;
+            }
+        }
+
+        return null;
+    }
+
+    protected int getCaretOffset(String fileContent, String caretLocation) {
+        int caretDelta = caretLocation.indexOf("^"); //$NON-NLS-1$
+        assertTrue(caretLocation, caretDelta != -1);
+
+        String caretContext = caretLocation.substring(0, caretDelta)
+                + caretLocation.substring(caretDelta + 1); // +1: skip "^"
+        int caretContextIndex = fileContent.indexOf(caretContext);
+        assertTrue("Caret content " + caretContext + " not found in file",
+                caretContextIndex != -1);
+        return caretContextIndex + caretDelta;
+    }
+
+    private void checkFormat(XmlFormatPreferences prefs, XmlFormatStyle style,
+            String xml, String expected, String delimiter) throws Exception {
+        checkFormat(prefs, style, xml, expected, delimiter, null, null);
+    }
+
     private void checkFormat(XmlFormatPreferences prefs, XmlFormatStyle style,
             String xml, String expected) throws Exception {
         checkFormat(prefs, style, xml, expected, "\n"); //$NON-NLS-1$
@@ -78,9 +139,9 @@
     public void testLayout1() throws Exception {
         checkFormat(
                 "<LinearLayout><Button></Button></LinearLayout>",
-                "<LinearLayout>\n" +
+                "<LinearLayout >\n" +
                 "\n" +
-                "    <Button>\n" +
+                "    <Button >\n" +
                 "    </Button>\n" +
                 "\n" +
                 "</LinearLayout>");
@@ -89,10 +150,9 @@
     public void testLayout2() throws Exception {
         checkFormat(
                 "<LinearLayout><Button foo=\"bar\"></Button></LinearLayout>",
-                "<LinearLayout>\n" +
+                "<LinearLayout >\n" +
                 "\n" +
-                "    <Button\n" +
-                "        foo=\"bar\">\n" +
+                "    <Button foo=\"bar\" >\n" +
                 "    </Button>\n" +
                 "\n" +
                 "</LinearLayout>");
@@ -104,9 +164,9 @@
         checkFormat(
                 prefs, XmlFormatStyle.LAYOUT,
                 "<LinearLayout><Button foo=\"bar\"></Button></LinearLayout>",
-                "<LinearLayout>\n" +
+                "<LinearLayout >\n" +
                 "\n" +
-                "    <Button foo=\"bar\">\n" +
+                "    <Button foo=\"bar\" >\n" +
                 "    </Button>\n" +
                 "\n" +
                 "</LinearLayout>");
@@ -191,9 +251,8 @@
                 "         SYSTEM \"http://www.xml.com/iso/isolat2-xml.entities\" >\n" +
                 "]>\n" +
                  */
-                "<LinearLayout\n" +
-                "    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
-                "    android:orientation=\"vertical\">\n" +
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                "    android:orientation=\"vertical\" >\n" +
                 "    <![CDATA[\n" +
                 "This is character data!\n" +
                 "<!-- This is not a comment! -->\n" +
@@ -218,13 +277,158 @@
         checkFormat(
                 XmlFormatPreferences.create(), XmlFormatStyle.LAYOUT,
                 "<LinearLayout><Button foo=\"bar\"></Button></LinearLayout>",
-                "<LinearLayout>\r\n" +
+                "<LinearLayout >\r\n" +
                 "\r\n" +
-                "    <Button\r\n" +
-                "        foo=\"bar\">\r\n" +
+                "    <Button foo=\"bar\" >\r\n" +
                 "    </Button>\r\n" +
                 "\r\n" +
                 "</LinearLayout>",
                 "\r\n");
     }
+
+    public void testRemoveBlanklines() throws Exception {
+        XmlFormatPreferences prefs = XmlFormatPreferences.create();
+        prefs.removeEmptyLines = true;
+        checkFormat(
+                prefs, XmlFormatStyle.LAYOUT,
+                "<foo><bar><baz1></baz1><baz2></baz2></bar><bar2></bar2><bar3><baz12></baz12></bar3></foo>",
+                "<foo >\n" +
+                "    <bar >\n" +
+                "        <baz1 >\n" +
+                "        </baz1>\n" +
+                "        <baz2 >\n" +
+                "        </baz2>\n" +
+                "    </bar>\n" +
+                "    <bar2 >\n" +
+                "    </bar2>\n" +
+                "    <bar3 >\n" +
+                "        <baz12 >\n" +
+                "        </baz12>\n" +
+                "    </bar3>\n" +
+                "</foo>");
+    }
+
+    public void testRange() throws Exception {
+        checkFormat(
+                XmlFormatPreferences.create(), XmlFormatStyle.LAYOUT,
+                "<LinearLayout><Button foo=\"bar\"></Button><CheckBox/></LinearLayout>",
+                "\n" +
+                "    <Button foo=\"bar\" >\n" +
+                "    </Button>\n" +
+                "\n" +
+                "    <CheckBox >\n" +
+                "    </CheckBox>\n",
+                "\n", "Button", "CheckBox");
+    }
+
+    public void testRange2() throws Exception {
+        XmlFormatPreferences prefs = XmlFormatPreferences.create();
+        prefs.removeEmptyLines = true;
+        checkFormat(
+                prefs, XmlFormatStyle.LAYOUT,
+                "<foo><bar><baz1></baz1><baz2></baz2></bar><bar2></bar2><bar3><baz12></baz12></bar3></foo>",
+                "        <baz1 >\n" +
+                "        </baz1>\n" +
+                "        <baz2 >\n" +
+                "        </baz2>\n" +
+                "    </bar>\n" +
+                "    <bar2 >\n" +
+                "    </bar2>\n" +
+                "    <bar3 >\n" +
+                "        <baz12 >\n" +
+                "        </baz12>\n",
+                "\n", "baz1", "baz12");
+    }
+
+    public void testEOLcomments() throws Exception {
+        checkFormat(
+                XmlFormatStyle.LAYOUT,
+                "<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
+                "    <item android:state_pressed=\"true\"\n" +
+                "          android:color=\"#ffff0000\"></item> <!-- pressed -->\n" +
+                "    <item android:state_focused=\"true\"\n" +
+                "          android:color=\"#ff0000ff\"></item> <!-- focused -->\n" +
+                "    <item android:color=\"#ff000000\"></item> <!-- default -->\n" +
+                "</selector>",
+
+                "<selector xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n" +
+                "\n" +
+                "    <item\n" +
+                "        android:color=\"#ffff0000\"\n" +
+                "        android:state_pressed=\"true\"></item> <!-- pressed -->\n" +
+                "\n" +
+                "    <item\n" +
+                "        android:color=\"#ff0000ff\"\n" +
+                "        android:state_focused=\"true\"></item> <!-- focused -->\n" +
+                "\n" +
+                "    <item android:color=\"#ff000000\"></item> <!-- default -->\n" +
+                "\n" +
+                "</selector>");
+    }
+
+    public void testPreserveNewlineAfterComment() throws Exception {
+        checkFormat(
+                XmlFormatStyle.RESOURCE,
+                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+                "<resources><dimen name=\"colorstrip_height\">6dip</dimen>\n" +
+                "    <!-- comment1 --><dimen name=\"title_height\">45dip</dimen>\n" +
+                "\n" +
+                "    <!-- comment2: newline above --><dimen name=\"now_playing_height\">90dip</dimen>\n" +
+                "    <dimen name=\"text_size_small\">14sp</dimen>\n" +
+                "\n" +
+                "\n" +
+                "    <!-- comment3: newline above and below -->\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "    <dimen name=\"text_size_medium\">18sp</dimen><dimen name=\"text_size_large\">22sp</dimen>\n" +
+                "</resources>",
+
+                "<resources>\n" +
+                "\n" +
+                "    <dimen name=\"colorstrip_height\">6dip</dimen>\n" +
+                "\n" +
+                "    <!-- comment1 -->\n" +
+                "    <dimen name=\"title_height\">45dip</dimen>\n" +
+                "\n" +
+                "    <!-- comment2: newline above -->\n" +
+                "    <dimen name=\"now_playing_height\">90dip</dimen>\n" +
+                "    <dimen name=\"text_size_small\">14sp</dimen>\n" +
+                "\n" +
+                "    <!-- comment3: newline above and below -->\n" +
+                "\n" +
+                "    <dimen name=\"text_size_medium\">18sp</dimen>\n" +
+                "    <dimen name=\"text_size_large\">22sp</dimen>\n" +
+                "\n" +
+                "</resources>");
+    }
+
+    public void testPlurals() throws Exception {
+        checkFormat(
+                XmlFormatStyle.RESOURCE,
+                "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" +
+                "<string name=\"toast_sync_error\">Sync error: <xliff:g id=\"error\">%1$s</xliff:g></string>\n" +
+                "<string name=\"session_subtitle\"><xliff:g id=\"time\">%1$s</xliff:g> in <xliff:g id=\"room\">%2$s</xliff:g></string>\n" +
+                "<plurals name=\"now_playing_countdown\">\n" +
+                "<item quantity=\"zero\"><xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "<item quantity=\"one\"><xliff:g id=\"number_of_days\">%1$s</xliff:g> day, <xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "<item quantity=\"other\"><xliff:g id=\"number_of_days\">%1$s</xliff:g> days, <xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "</plurals>\n" +
+                "</resources>",
+
+                "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" +
+                "\n" +
+                "    <string name=\"toast_sync_error\">Sync error: <xliff:g id=\"error\">%1$s</xliff:g></string>\n" +
+                "    <string name=\"session_subtitle\"><xliff:g id=\"time\">%1$s</xliff:g> in <xliff:g id=\"room\">%2$s</xliff:g></string>\n" +
+                "\n" +
+                "    <plurals name=\"now_playing_countdown\">\n" +
+                "        <item quantity=\"zero\"><xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "        <item quantity=\"one\"><xliff:g id=\"number_of_days\">%1$s</xliff:g> day, <xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "        <item quantity=\"other\"><xliff:g id=\"number_of_days\">%1$s</xliff:g> days, <xliff:g id=\"remaining_time\">%2$s</xliff:g></item>\n" +
+                "    </plurals>\n" +
+                "\n" +
+                "</resources>");
+    }
+
+
 }