#62006: normalize whitespace in XMLUtil.write, to guard against weird blocks of whitespace being shuffled around
by AntProjectHelper's defensive cloning. Fix should correct the bug and even heal previously broken project.xml
files, if they are modified in some way.
1.1 --- a/openide.util/src/org/openide/xml/XMLUtil.java Tue Oct 04 09:18:56 2005 +0000
1.2 +++ b/openide.util/src/org/openide/xml/XMLUtil.java Wed Oct 05 00:53:06 2005 +0000
1.3 @@ -35,6 +35,10 @@
1.4 import org.w3c.dom.DOMImplementation;
1.5 import org.w3c.dom.Document;
1.6 import org.w3c.dom.DocumentType;
1.7 +import org.w3c.dom.Element;
1.8 +import org.w3c.dom.Node;
1.9 +import org.w3c.dom.NodeList;
1.10 +import org.w3c.dom.Text;
1.11 import org.xml.sax.EntityResolver;
1.12 import org.xml.sax.ErrorHandler;
1.13 import org.xml.sax.InputSource;
1.14 @@ -357,11 +361,12 @@
1.15 if (enc == null) {
1.16 throw new NullPointerException("You must set an encoding; use \"UTF-8\" unless you have a good reason not to!"); // NOI18N
1.17 }
1.18 + Document doc2 = normalize(doc);
1.19 if (System.getProperty("java.specification.version").startsWith("1.4")) { // NOI18N
1.20 // Hack for JDK 1.4. Using JAXP won't work; e.g. JDK bug #6308026.
1.21 // Try using Xerces instead - let's hope it's loadable...
1.22 try {
1.23 - writeXerces(doc, out, enc);
1.24 + writeXerces(doc2, out, enc);
1.25 return;
1.26 } catch (ClassNotFoundException e) {
1.27 throw (IOException) new IOException("You need to have xerces.jar available to use XMLUtil.write under JDK 1.4: " + e).initCause(e); // NOI18N
1.28 @@ -373,7 +378,7 @@
1.29 try {
1.30 Transformer t = TransformerFactory.newInstance().newTransformer(
1.31 new StreamSource(new StringReader(IDENTITY_XSLT_WITH_INDENT)));
1.32 - DocumentType dt = doc.getDoctype();
1.33 + DocumentType dt = doc2.getDoctype();
1.34 if (dt != null) {
1.35 String pub = dt.getPublicId();
1.36 if (pub != null) {
1.37 @@ -382,7 +387,7 @@
1.38 t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dt.getSystemId());
1.39 }
1.40 t.setOutputProperty(OutputKeys.ENCODING, enc);
1.41 - Source source = new DOMSource(doc);
1.42 + Source source = new DOMSource(doc2);
1.43 Result result = new StreamResult(out);
1.44 t.transform(source, result);
1.45 } catch (Exception e) {
1.46 @@ -695,4 +700,43 @@
1.47
1.48 return SAXParserFactory.newInstance();
1.49 }
1.50 +
1.51 + /**
1.52 + * Try to normalize a document by removing nonsignificant whitespace.
1.53 + * @see "#62006"
1.54 + */
1.55 + private static Document normalize(Document orig) throws IOException {
1.56 + if (orig.getChildNodes().getLength() != 1) {
1.57 + // Don't mess around with it.
1.58 + return orig;
1.59 + }
1.60 + DocumentBuilder builder = (DocumentBuilder) builderTL[0].get();
1.61 + if (builder == null) {
1.62 + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1.63 + factory.setValidating(false);
1.64 + factory.setNamespaceAware(false);
1.65 + try {
1.66 + builder = factory.newDocumentBuilder();
1.67 + } catch (ParserConfigurationException e) {
1.68 + throw (IOException) new IOException("Cannot create parser satisfying configuration parameters: " + e).initCause(e); //NOI18N
1.69 + }
1.70 + builderTL[0].set(builder);
1.71 + }
1.72 + Document doc = builder.newDocument();
1.73 + doc.appendChild(doc.importNode(orig.getDocumentElement(), true));
1.74 + doc.normalize();
1.75 + NodeList nl = doc.getElementsByTagName("*"); // NOI18N
1.76 + for (int i = 0; i < nl.getLength(); i++) {
1.77 + Element e = (Element) nl.item(i);
1.78 + NodeList nl2 = e.getChildNodes();
1.79 + for (int j = 0; j < nl2.getLength(); j++) {
1.80 + Node n = nl2.item(j);
1.81 + if (n instanceof Text && ((Text) n).getNodeValue().trim().length() == 0) {
1.82 + e.removeChild(n);
1.83 + j--; // since list is dynamic
1.84 + }
1.85 + }
1.86 + }
1.87 + return doc;
1.88 + }
1.89 }
2.1 --- a/openide.util/test/unit/src/org/openide/xml/XMLUtilTest.java Tue Oct 04 09:18:56 2005 +0000
2.2 +++ b/openide.util/test/unit/src/org/openide/xml/XMLUtilTest.java Wed Oct 05 00:53:06 2005 +0000
2.3 @@ -18,6 +18,7 @@
2.4 import java.io.CharConversionException;
2.5 import java.io.IOException;
2.6 import java.io.StringReader;
2.7 +import javax.xml.parsers.DocumentBuilderFactory;
2.8 import junit.framework.Test;
2.9 import org.netbeans.junit.NbTestCase;
2.10 import org.netbeans.junit.NbTestSuite;
2.11 @@ -279,4 +280,41 @@
2.12 assertTrue("had reasonable indentation in\n" + data, data.indexOf("<root>\n <child/>\n</root>\n") != -1);
2.13 }
2.14
2.15 + /** cf. #62006 */
2.16 + public void testIndentation2() throws Exception {
2.17 + String data =
2.18 + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
2.19 + "<p>\n" +
2.20 + " <t/>\n" +
2.21 + " <c>\n" +
2.22 + " <d>\n" +
2.23 + " <s/>\n" +
2.24 + " </d>\n" +
2.25 + " </c>\n" +
2.26 + "</p>\n";
2.27 + Document doc = XMLUtil.parse(new InputSource(new StringReader(data)), false, false, null, null);
2.28 + Element d = (Element) doc.getElementsByTagName("d").item(0);
2.29 + Element c = (Element) d.getParentNode();
2.30 + Element d2 = (Element) DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().importNode(d, true);
2.31 + c.removeChild(d);
2.32 + c.appendChild(doc.importNode(d2, true));
2.33 + ByteArrayOutputStream baos = new ByteArrayOutputStream();
2.34 + XMLUtil.write(doc, baos, "UTF-8");
2.35 + String data2 = baos.toString().replaceAll("\r\n", "\n");
2.36 + assertEquals("identity replacement should not mess up indentation", data, data2);
2.37 + }
2.38 +
2.39 + public void testSignificantWhitespace() throws Exception {
2.40 + String data =
2.41 + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
2.42 + "<r>\n" +
2.43 + " <p>This is <em>not</em> a test!</p>\n" +
2.44 + "</r>\n";
2.45 + Document doc = XMLUtil.parse(new InputSource(new StringReader(data)), false, false, null, null);
2.46 + ByteArrayOutputStream baos = new ByteArrayOutputStream();
2.47 + XMLUtil.write(doc, baos, "UTF-8");
2.48 + String data2 = baos.toString().replaceAll("\r\n", "\n");
2.49 + assertEquals("identity replacement should not mess up significant whitespace", data, data2);
2.50 + }
2.51 +
2.52 }