/*
* 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" +
"");
}
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");
}
}