[358545] Formatting a JSP file, containing Java snippets, leads to misformatted or even lost code
diff --git a/tests/org.eclipse.jst.jsp.ui.tests/src/org/eclipse/jst/jsp/ui/tests/format/TestContentFormatter.java b/tests/org.eclipse.jst.jsp.ui.tests/src/org/eclipse/jst/jsp/ui/tests/format/TestContentFormatter.java index d880b15..173ef6f 100644 --- a/tests/org.eclipse.jst.jsp.ui.tests/src/org/eclipse/jst/jsp/ui/tests/format/TestContentFormatter.java +++ b/tests/org.eclipse.jst.jsp.ui.tests/src/org/eclipse/jst/jsp/ui/tests/format/TestContentFormatter.java
@@ -11,6 +11,7 @@ package org.eclipse.jst.jsp.ui.tests.format; import java.io.ByteArrayOutputStream; +import java.io.CharArrayWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -22,7 +23,10 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Preferences; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.formatter.FormattingContext; import org.eclipse.jface.text.formatter.FormattingContextProperties; @@ -31,19 +35,18 @@ import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP; import org.eclipse.jst.jsp.ui.tests.util.ProjectUtil; -import org.eclipse.jst.jsp.ui.tests.util.StringCompareUtil; import org.eclipse.wst.html.core.internal.HTMLCorePlugin; import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.utils.StringUtils; public class TestContentFormatter extends TestCase { String wtp_autotest_noninteractive = null; private static final String PROJECT_NAME = "jspformatting"; private static final String UTF_8 = "UTF-8"; - private StringCompareUtil fStringCompareUtil; private IFormattingContext fContext; protected void setUp() throws Exception { @@ -62,8 +65,6 @@ fContext = new FormattingContext(); fContext.setProperty(FormattingContextProperties.CONTEXT_DOCUMENT, Boolean.valueOf(true)); - - fStringCompareUtil = new StringCompareUtil(); } private void formatAndAssertEquals(String beforePath, String afterPath, boolean resetPreferences) throws UnsupportedEncodingException, IOException, CoreException { @@ -95,8 +96,15 @@ afterModel.save(afterBytes); String expectedContents = new String(afterBytes.toByteArray(), UTF_8); + expectedContents = StringUtils.replace(expectedContents, "\r\n", "\n"); + expectedContents = StringUtils.replace(expectedContents, "\r", "\n"); + String actualContents = new String(formattedBytes.toByteArray(), UTF_8); - assertTrue("Formatted document differs from the expected.\nExpected Contents:\n" + expectedContents + "\nActual Contents:\n" + actualContents, fStringCompareUtil.equalsIgnoreLineSeperator(expectedContents, actualContents)); + actualContents = StringUtils.replace(actualContents, "\r\n", "\n"); + actualContents = StringUtils.replace(actualContents, "\r", "\n"); + + assertEquals("Formatted document differs from the expected.", expectedContents, actualContents); + assertTrue(onlyWhiteSpaceDiffers(expectedContents, actualContents)); } finally { if (beforeModel != null) @@ -106,6 +114,45 @@ } } + private void formatAndAssertSignificantEquals(String beforePath, boolean resetPreferences) throws UnsupportedEncodingException, IOException, CoreException { + IStructuredModel beforeModel = null; + try { + beforeModel = getModelForEdit(beforePath); + assertNotNull("could not retrieve structured model for : " + beforePath, beforeModel); + + if (resetPreferences) { + resetPreferencesToDefault(); + } + + SourceViewerConfiguration configuration = new StructuredTextViewerConfigurationJSP(); + IContentFormatterExtension formatter = (IContentFormatterExtension) configuration.getContentFormatter(null); + + IDocument document = beforeModel.getStructuredDocument(); + String before = document.get(); + Region region = new Region(0, document.getLength()); + fContext.setProperty(FormattingContextProperties.CONTEXT_DOCUMENT, Boolean.TRUE); + fContext.setProperty(FormattingContextProperties.CONTEXT_REGION, region); + DocumentRewriteSession rewriteSession = null; + if (document instanceof IDocumentExtension4) { + IDocumentExtension4 extension = (IDocumentExtension4) document; + DocumentRewriteSessionType type = DocumentRewriteSessionType.UNRESTRICTED; + rewriteSession = (extension.getActiveRewriteSession() != null) ? null : extension.startRewriteSession(type); + } + formatter.format(document, fContext); + String after = document.get(); + if (document instanceof IDocumentExtension4 && rewriteSession != null) { + IDocumentExtension4 extension = (IDocumentExtension4) document; + extension.stopRewriteSession(rewriteSession); + } + + assertTrue(onlyWhiteSpaceDiffers(before, after)); + } + finally { + if (beforeModel != null) + beforeModel.releaseFromEdit(); + } + } + /** * must release model (from edit) after * @@ -130,6 +177,40 @@ return model; } + /** + * Useful for making sure all significant content was retained. + * + * @param expectedContents + * @param actualContents + * @return + */ + private boolean onlyWhiteSpaceDiffers(String expectedContents, String actualContents) { + CharArrayWriter writer1 = new CharArrayWriter(); + char[] expected = expectedContents.toCharArray(); + for (int i = 0; i < expected.length; i++) { + if (!Character.isWhitespace(expected[i])) + writer1.write(expected[i]); + } + + CharArrayWriter writer2 = new CharArrayWriter(); + char[] actual = actualContents.toCharArray(); + for (int i = 0; i < actual.length; i++) { + if (!Character.isWhitespace(actual[i])) + writer2.write(actual[i]); + } + writer1.close(); + writer2.close(); + + char[] expectedCompacted = writer1.toCharArray(); + char[] actualCompacted = writer1.toCharArray(); + assertEquals("significant character lengths are not the same", expectedCompacted.length, actualCompacted.length); + for (int i = 0; i < actualCompacted.length; i++) { + assertEquals("significant character differs", expectedCompacted[i], actualCompacted[i]); + } + + return true; + } + private void resetPreferencesToDefault() { Preferences preferences = HTMLCorePlugin.getDefault().getPluginPreferences(); preferences.setToDefault(HTMLCorePreferenceNames.SPLIT_MULTI_ATTRS); @@ -168,4 +249,8 @@ String afterPath = "/" + PROJECT_NAME + "/WebContent/formatbug102495_4-fmt.jsp"; formatAndAssertEquals(beforePath, afterPath, true); } + + public void testFormatBug358545() throws UnsupportedEncodingException, IOException, CoreException { + formatAndAssertSignificantEquals("/" + PROJECT_NAME + "/WebContent/formatbug358545.jsp", true); + } }
diff --git a/tests/org.eclipse.jst.jsp.ui.tests/testfiles/jspformatting/WebContent/formatbug358545.jsp b/tests/org.eclipse.jst.jsp.ui.tests/testfiles/jspformatting/WebContent/formatbug358545.jsp new file mode 100644 index 0000000..622877c --- /dev/null +++ b/tests/org.eclipse.jst.jsp.ui.tests/testfiles/jspformatting/WebContent/formatbug358545.jsp
@@ -0,0 +1,92 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="com.eprice.data.*, + com.eprice.data.PricingPlanAttribute.AttributeSourceEnum, + com.eprice.data.Currency, + com.epriceadmin.form.*, + java.util.Iterator, + com.epriceadmin.service.PriceBookService, + com.epriceadmin.data.*, + com.eprice.data.pricebook.*, + com.eprice.importsexports.data.AttributeValue, + java.util.*, + java.text.*" +%> +<html> +<head> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="eprice" uri="WEB-INF/tld/eprice.tld"%> +<jsp:useBean id="AdminSession" scope="session" type="com.epriceadmin.data.AdminSession" /> +<c:set var="activeForm" value="${AdminSession.activeForm}" scope="session" /> +<script type="text/javascript"> + var oldValue = ""; + + function copyPrices(prevValue) { + var pricetxts = document.forms[0].priceField; + if (isNaN(pricetxts[0].value)) { + alert('Cannot copy non-numeric prices'); + return; + } + for ( var i = 0; i < pricetxts.length; i++) { + if (!isNaN(pricetxts[0].value)) { + pricetxts[i].value = pricetxts[0].value + oldValue = prevValue + } + } + + } + + + function controlForm() { +<% if( priceBooksEditIndividualForm.getShowAttributes() != null ) { %> + document.getElementById("savePrices").className = "buttonDisabled"; + document.getElementById("savePrices").mouseover.className = "buttonDisabled"; +<% } %> + } + + function MM_findObj(n, d) { //v4.01 + var p, i, x; + if (!d) + d = document; + if ((p = n.indexOf("?")) > 0 && parent.frames.length) { + d = parent.frames[n.substring(p + 1)].document; + n = n.substring(0, p); + } + if (!(x = d[n]) && d.all) + x = d.all[n]; + for (i = 0; !x && i < d.forms.length; i++) + x = d.forms[i][n]; + for (i = 0; !x && d.layers && i < d.layers.length; i++) + x = MM_findObj(n, d.layers[i].document); + if (!x && d.getElementById) + x = d.getElementById(n); + return x; + } + + function MM_showHideLayers() { //v6.0 + var i, p, v, obj, args = MM_showHideLayers.arguments; + for (i = 0; i < (args.length - 2); i += 3) + if ((obj = MM_findObj(args[i])) != null) { + v = args[i + 2]; + if (obj.style) { + obj = obj.style; + v = (v == 'show') ? 'visible' : (v == 'hide') ? 'hidden' + : v; + } + obj.visibility = v; + } + } + function verifyLocalAcessPLI() { +<% if( request.getAttribute( "LOCAL_DIVERSE_ACESS_PLI" ) != null ) {%> + MM_showHideLayers('restrictLocalAccessPLIPopup', '', 'show'); +<% }%> + } +</script> +<title>ePrice Administration - Price Books - Edit Individual Prices</title> +</head> +<body onkeydown="selectSave()" onload="verifyLocalAcessPLI();"> + + + +</body> +</html>