/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.editors.formatting; import com.android.ide.common.xml.XmlFormatStyle; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import org.eclipse.jface.preference.PreferenceStore; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import junit.framework.TestCase; @SuppressWarnings({ "javadoc", "restriction" }) public class EclipseXmlPrettyPrinterTest 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); EclipseXmlFormatPreferences formatPrefs = EclipseXmlFormatPreferences.create(); assertTrue(formatPrefs.oneAttributeOnFirstLine); } private void checkFormat(EclipseXmlFormatPreferences prefs, String baseLocation, String xml, String expected, String delimiter, String startNodeName, boolean openTagOnly, String endNodeName) throws Exception { IStructuredModel model = DomUtilities.createStructuredModel(xml); assertNotNull(model); model.setBaseLocation(baseLocation); Document document = null; if (model instanceof IDOMModel) { IDOMModel domModel = (IDOMModel) model; document = domModel.getDocument(); } else { fail("Can't get DOM model"); return; } XmlFormatStyle style = AndroidXmlFormattingStrategy.guessStyle(model, document); EclipseXmlPrettyPrinter printer = new EclipseXmlPrettyPrinter(prefs, style, delimiter); printer.setEndWithNewline(xml.endsWith("\n")); StringBuilder sb = new StringBuilder(1000); 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, false/*openTagOnly*/); String formatted = sb.toString(); if (!expected.equals(formatted)) { System.out.println(formatted); } 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(EclipseXmlFormatPreferences prefs, String baseLocation, String xml, String expected, String delimiter) throws Exception { checkFormat(prefs, baseLocation, xml, expected, delimiter, null, false, null); } private void checkFormat(EclipseXmlFormatPreferences prefs, String baseLocation, String xml, String expected) throws Exception { checkFormat(prefs, baseLocation, xml, expected, "\n"); //$NON-NLS-1$ } private void checkFormat(String baseLocation, String xml, String expected) throws Exception { EclipseXmlFormatPreferences prefs = EclipseXmlFormatPreferences.create(); checkFormat(prefs, baseLocation, xml, expected); } public void testLayout1() throws Exception { checkFormat( "res/layout-port/layout1.xml", "", "\n" + "\n" + " \n" + "\n" + ""); } public void testLayout2() throws Exception { checkFormat( "res/layout-port/layout2.xml", "", "\n" + "\n" + " \n" + "\n" + ""); } public void testLayout3() throws Exception { EclipseXmlFormatPreferences prefs = EclipseXmlFormatPreferences.create(); prefs.oneAttributeOnFirstLine = true; checkFormat( prefs, "res/layout-land/layout3.xml", "", "\n" + "\n" + " \n" + "\n" + ""); } public void testClosedElements() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + "\n" + "\n", "\n" + "\n" + " \n" + " \n" + "\n" + "\n"); } public void testResources() throws Exception { checkFormat( "res/values-us/strings.xml", "Text value here ", "\n\n" + " Text value here \n" + "\n"); } public void testNodeTypes() throws Exception { // Ensures that a document with all kinds of node types is serialized correctly checkFormat( "res/layout-xlarge/layout.xml", "\n" + "\n" + "\n" + "\n" + "]>\n" + "\n" + "\n" + "and \n" + "]]> \n" + "This is text: < and &\n" + "\n" + "\n" + "%ISOLat2; \n" + " \n" + "", "\n" + "\n" + "\n" + "\n" + "]>\n" + "\n" + "\n" + "and \n" + "]]>\n" + "This is text: < and &\n" + " \n" + " \n" + "%ISOLat2; \n" + "\n" + "\n" + ""); } public void testWindowsDelimiters() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/layout-xlarge/layout.xml", "", "\r\n" + "\r\n" + " \r\n" + "\r\n" + "", "\r\n"); } public void testRemoveBlanklines() throws Exception { EclipseXmlFormatPreferences prefs = EclipseXmlFormatPreferences.create(); prefs.removeEmptyLines = true; checkFormat( prefs, "res/layout-xlarge/layout.xml", "", "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""); } public void testRange() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/layout-xlarge/layout.xml", "", "\n" + " \n" + "\n" + " \n", "\n", "Button", false, "CheckBox"); } public void testOpenTagOnly() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/layout-xlarge/layout.xml", "", "\n" + " \n", "\n", "Button", true, "Button"); } public void testRange2() throws Exception { EclipseXmlFormatPreferences prefs = EclipseXmlFormatPreferences.create(); prefs.removeEmptyLines = true; checkFormat( prefs, "res/layout-xlarge/layout.xml", "", " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n", "\n", "baz1", false, "baz12"); } public void testEOLcomments() throws Exception { checkFormat( "res/drawable-mdpi/states.xml", "\n" + " \n" + " \n" + " \n" + "", "\n" + "\n" + " \n" + " \n" + " \n" + "\n" + ""); } public void testFormatColorList() throws Exception { checkFormat( "res/drawable-hdpi/states.xml", "\n" + "\n" + " \n" + "", "\n" + "\n" + " \n" + " \n" + "\n" + ""); } public void testPreserveNewlineAfterComment() throws Exception { checkFormat( "res/values/dimen.xml", "\n" + "6dip\n" + " 45dip\n" + "\n" + " 90dip\n" + " 14sp\n" + "\n" + "\n" + " \n" + "\n" + "\n" + "\n" + " 18sp22sp\n" + "", "\n" + "\n" + "\n" + " 6dip\n" + " \n" + " 45dip\n" + "\n" + " \n" + " 90dip\n" + " 14sp\n" + "\n" + " \n" + "\n" + " 18sp\n" + " 22sp\n" + "\n" + ""); } public void testPlurals() throws Exception { checkFormat( "res/values-us/strings.xml", "\n" + "Sync error: %1$s\n" + "%1$s in %2$s\n" + "\n" + "%2$s\n" + "%1$s day, %2$s\n" + "%1$s days, %2$s\n" + "\n" + "", "\n" + "\n" + " Sync error: %1$s\n" + " %1$s in %2$s\n" + "\n" + " \n" + " %2$s\n" + " %1$s day, %2$s\n" + " %1$s days, %2$s\n" + " \n" + "\n" + ""); } public void testMultiAttributeResource() throws Exception { checkFormat( "res/values-us/strings.xml", "Enable extra debug logging?", "\n" + "\n" + " Enable extra debug logging?\n" + "\n" + ""); } public void testMultilineCommentAlignment() throws Exception { checkFormat( "res/values-us/strings.xml", "" + " \n" + " \n" + " \n" + "", "\n" + "\n" + " \n" + " \n" + " \n" + "\n" + ""); } public void testLineCommentSpacing() throws Exception { checkFormat( "res/values-us/strings.xml", "\n" + "\n" + " 6dip\n" + " \n" + " 45dip\n" + " \n" + " 90dip\n" + " 14sp\n" + "\n" + " \n" + "\n" + " 18sp\n" + " 22sp\n" + "\n" + "", "\n" + "\n" + " 6dip\n" + " \n" + " 45dip\n" + " \n" + " 90dip\n" + " 14sp\n" + "\n" + " \n" + "\n" + " 18sp\n" + " 22sp\n" + "\n" + ""); } public void testCommentHandling() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/layout/layout1.xml", "\n" + "\n" + " \n" + "\n" + " \n" + " \n" + "\n" + "\n" + "", "\n" + "\n" + " \n" + "\n" + "\n" + " \n" + "\n" + "\n" + " \n" + "\n" + ""); } public void testCommentHandling2() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/layout-xlarge/layout.xml", "\n" + " \n" + "\n" + " \n" + "", "\n" + "\n" + " \n" + "\n" + " \n" + "\n" + ""); } public void testMenus1() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/menu/menu1.xml", // http://code.google.com/p/android/issues/detail?id=21383 "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + ""); } public void testMenus2() throws Exception { EclipseXmlFormatPreferences prefs = EclipseXmlFormatPreferences.create(); prefs.removeEmptyLines = true; checkFormat( prefs, "res/drawable-hdpi/layerlist.xml", // http://code.google.com/p/android/issues/detail?id=21346 "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""); } public void testMenus3() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/menu/menu1.xml", // http://code.google.com/p/android/issues/detail?id=21227 "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + "", "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + ""); } public void testColors1() throws Exception { checkFormat( EclipseXmlFormatPreferences.create(), "res/values/colors.xml", "\n" + " #99e21f14\n" + "\n" + " #99000000\n" + "", "\n" + "\n" + " #99e21f14\n" + " #99000000\n" + "\n" + ""); } public void testEclipseFormatStyle1() throws Exception { EclipseXmlFormatPreferences prefs = new EclipseXmlFormatPreferences() { @Override public String getOneIndentUnit() { return "\t"; } @Override public int getTabWidth() { return 8; } }; checkFormat( prefs, "res/values/colors.xml", "\n" + " #99e21f14\n" + "\n" + " #99000000\n" + "", "\n" + "\n" + "\t#99e21f14\n" + "\t#99000000\n" + "\n" + ""); } public void testEclipseFormatStyle2() throws Exception { EclipseXmlFormatPreferences prefs = new EclipseXmlFormatPreferences() { @Override public String getOneIndentUnit() { return " "; } @Override public int getTabWidth() { return 2; } }; prefs.useEclipseIndent = true; checkFormat( prefs, "res/values/colors.xml", "\n" + " #99e21f14\n" + "\n" + " #99000000\n" + "", "\n" + "\n" + " #99e21f14\n" + " #99000000\n" + "\n" + ""); } public void testNameSorting() throws Exception { checkFormat( "res/values/dimen.xml", "\n" + "\n" + " \n" + "", "\n" + "\n" + "\n" + " \n" + "\n" + ""); } public void testStableText() throws Exception { checkFormat( "res/layout/stable.xml", "\n" + "\n" + " Hello World\n" + "\n" + "", "\n" + "\n" + " Hello World\n" + "\n" + ""); } public void testResources1() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + "\n" + " a\n" + " \n" + "\n" + "", "\n" + "\n" + "\n" + " a\n" + "\n" + ""); } public void testMarkup() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + "\n" + "Welcome to Android!" + "To install a 24 Clock Widget, " + "please long press in Home Screen. Configure the Global Settings " + "here." + "" + "\n" + "", "\n" + "\n" + "\n" + " Welcome to Android!\n" + " To install a 24 Clock Widget, " + "please long press in Home Screen. Configure the Global Settings " + "here.\n" + "\n" + ""); } public void testPreserveEntities() throws Exception { // Ensure that entities such as > in the input string are preserved in the output // format checkFormat( "res/values/strings.xml", "\n" + "<untitled2>\n" + "<untitled2>\n" + "'untitled3"\n", "\n" + "\n" + "\n" + " <untitled2>\n" + " <untitled2>\n" + " 'untitled3"\n" + "\n" + "\n"); } public void testCData1() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + " \n" + "", "\n" + "\n" + "\n" + " \n" + "\n" + ""); } public void testCData2() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + " \n" + " \n" + "", "\n" + "\n" + "\n" + " \n" + "\n" + " \n" + " \n" + "\n" + ""); } public void testComplexString() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + "The database has " + "successfully been exported into:

" + "\\\"%s\\\"
" + "
", "\n" + "\n" + "\n" + " The database has " + "successfully been exported into:

" + "\\\"%s\\\"
\n" + "\n" + "
"); } public void test52887() throws Exception { // https://code.google.com/p/android/issues/detail?id=52887 checkFormat( "res/layout/relative.xml", "\n" + "\n", "\n" + "\n"); } public void testPreserveLastNewline() throws Exception { checkFormat( "res/values/strings.xml", "\n" + "\n" + "The database has " + "successfully been exported into:

" + "\\\"%s\\\"
" + "
\n", "\n" + "\n" + "\n" + " The database has " + "successfully been exported into:

" + "\\\"%s\\\"
\n" + "\n" + "
\n"); } }