diff --git a/.idea/dom4j.iml b/.idea/dom4j.iml index d48fb577..a6263003 100644 --- a/.idea/dom4j.iml +++ b/.idea/dom4j.iml @@ -1,10 +1,11 @@ - + + diff --git a/.travis.yml b/.travis.yml index 02da3a16..6fa53918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: java sudo: false jdk: - oraclejdk8 - - oraclejdk7 - - openjdk6 + - openjdk7 after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/build.gradle b/build.gradle index 390ffb7a..9109a005 100644 --- a/build.gradle +++ b/build.gradle @@ -124,5 +124,5 @@ if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword') } task wrapper(type: Wrapper) { - gradleVersion = '4.1' + gradleVersion = '4.1' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 082f14af..7215798d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 22 18:47:11 BST 2017 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip +#Sat Sep 16 13:35:18 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/src/main/java/org/dom4j/io/XMLWriter.java b/src/main/java/org/dom4j/io/XMLWriter.java index 4a9612bb..56742d39 100644 --- a/src/main/java/org/dom4j/io/XMLWriter.java +++ b/src/main/java/org/dom4j/io/XMLWriter.java @@ -42,6 +42,9 @@ import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.XMLFilterImpl; +import static org.dom4j.util.StringUtils.endsWithWhitespace; +import static org.dom4j.util.StringUtils.startsWithWhitespace; + /** *

* XMLWriter takes a DOM4J tree and formats it to a stream as @@ -1036,14 +1039,16 @@ protected void writeElementContent(Element element) throws IOException { if (!textOnly && format.isPadText()) { // only add the PAD_TEXT if the text itself starts with // whitespace - char firstChar = 'a'; + final boolean startsWithWhitespace; if (buff != null) { - firstChar = buff.charAt(0); + startsWithWhitespace = startsWithWhitespace(buff); } else if (lastTextNode != null) { - firstChar = lastTextNode.getText().charAt(0); + startsWithWhitespace = startsWithWhitespace(lastTextNode.getText()); + } else { + startsWithWhitespace = false; } - if (Character.isWhitespace(firstChar)) { + if (startsWithWhitespace) { writer.write(PAD_TEXT); } } @@ -1059,15 +1064,14 @@ protected void writeElementContent(Element element) throws IOException { if (format.isPadText()) { // only add the PAD_TEXT if the text itself ends // with whitespace - char lastTextChar = 'a'; + final boolean endsWithWhitespace; if (buff != null) { - lastTextChar = buff.charAt(buff.length() - 1); - } else if (lastTextNode != null) { - String txt = lastTextNode.getText(); - lastTextChar = txt.charAt(txt.length() - 1); + endsWithWhitespace = endsWithWhitespace(buff); + } else { + endsWithWhitespace = endsWithWhitespace(lastTextNode.getText()); } - if (Character.isWhitespace(lastTextChar)) { + if (endsWithWhitespace) { writer.write(PAD_TEXT); } } @@ -1084,14 +1088,14 @@ protected void writeElementContent(Element element) throws IOException { if (!textOnly && format.isPadText()) { // only add the PAD_TEXT if the text itself starts with // whitespace - char firstChar = 'a'; + final boolean startsWithWhitespace; if (buff != null) { - firstChar = buff.charAt(0); + startsWithWhitespace = startsWithWhitespace(buff); } else { - firstChar = lastTextNode.getText().charAt(0); + startsWithWhitespace = startsWithWhitespace(lastTextNode.getText()); } - if (Character.isWhitespace(firstChar)) { + if (startsWithWhitespace) { writer.write(PAD_TEXT); } } @@ -1116,10 +1120,7 @@ protected void writeElementContent(Element element) throws IOException { if ((lastTextNode != null) && format.isPadText()) { // only add the PAD_TEXT if the text itself ends with // whitespace - String txt = lastTextNode.getText(); - char lastTextChar = txt.charAt(txt.length() - 1); - - if (Character.isWhitespace(lastTextChar)) { + if (endsWithWhitespace(lastTextNode.getText())) { writer.write(PAD_TEXT); } } diff --git a/src/main/java/org/dom4j/util/StringUtils.java b/src/main/java/org/dom4j/util/StringUtils.java new file mode 100644 index 00000000..694e3b99 --- /dev/null +++ b/src/main/java/org/dom4j/util/StringUtils.java @@ -0,0 +1,44 @@ +package org.dom4j.util; + +/** + * Contains utilities related to strings. + * + * @author Marián Petráš + */ +public final class StringUtils { + + private StringUtils() {} + + /** + * Finds out if the given character sequence starts with a whitespace + * character. + * + * @return {@code true} if the given character sequence is not empty + * and starts with a whitespace character; {@code false} otherwise + * @exception NullPointerException if the given character sequence is + * {@code null} + */ + public static boolean startsWithWhitespace(final CharSequence charSeq) { + if (charSeq.length() == 0) { + return false; + } + return Character.isWhitespace(charSeq.charAt(0)); + } + + /** + * Finds out if the given character sequence ends with a whitespace + * character. + * + * @return {@code true} if the given character sequence is not empty + * and ends with a whitespace character; {@code false} otherwise + * @exception NullPointerException if the given character sequence is + * {@code null} + */ + public static boolean endsWithWhitespace(final CharSequence charSeq) { + if (charSeq.length() == 0) { + return false; + } + return Character.isWhitespace(charSeq.charAt(charSeq.length() - 1)); + } + +} diff --git a/src/test/java/org/dom4j/XMLWriterTest.java b/src/test/java/org/dom4j/XMLWriterTest.java index 92e8c884..ecf0d34c 100644 --- a/src/test/java/org/dom4j/XMLWriterTest.java +++ b/src/test/java/org/dom4j/XMLWriterTest.java @@ -7,6 +7,8 @@ package org.dom4j; +import org.dom4j.dom.DOMElement; +import org.dom4j.dom.DOMText; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; @@ -18,6 +20,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.CharArrayWriter; +import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; @@ -502,6 +506,77 @@ public void testNullCData() { System.out.println(doc.asXML()); } + public void testGitHubIssue26_case1() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMElement("elem1")); + element.add(new DOMText("")); + element.add(new DOMText("")); + element.add(new DOMElement("elem2")); + testGitHubIssue26(element); + } + + public void testGitHubIssue26_case2() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMElement("elem1")); + element.add(new DOMText("")); + element.add(new DOMElement("elem2")); + testGitHubIssue26(element); + } + + public void testGitHubIssue26_case3() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMText("")); + element.add(new DOMText("")); + element.add(new DOMElement("elem")); + testGitHubIssue26(element); + } + + public void testGitHubIssue26_case4() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMText("")); + element.add(new DOMElement("elem")); + testGitHubIssue26(element); + } + + public void testGitHubIssue26_case5() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMElement("elem")); + element.add(new DOMText("")); + element.add(new DOMText("")); + testGitHubIssue26(element); + } + + public void testGitHubIssue26_case6() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMElement("elem")); + element.add(new DOMText("")); + testGitHubIssue26(element); + } + + private void testGitHubIssue26(final Element element) throws IOException { + final OutputFormat format = new OutputFormat(" ", true); + format.setSuppressDeclaration(false); + format.setTrimText(true); + format.setPadText(true); + format.setNewlines(true); + + new XMLWriter(new CharArrayWriter(128), format).write(element); + } + + public void testGitHubIssue26_case7() throws IOException { + final Element element = new DOMElement("foo"); + element.add(new DOMText("")); + element.add(new DOMElement("elem")); + + final OutputFormat format = new OutputFormat(" ", true); + format.setSuppressDeclaration(false); + format.setTrimText(false); + format.setPadText(true); + format.setNewlines(true); + + new XMLWriter(new CharArrayWriter(128), format).write(element); + } + protected void generateXML(ContentHandler handler) throws SAXException { handler.startDocument(); diff --git a/src/test/java/org/dom4j/util/StringUtilsTest.java b/src/test/java/org/dom4j/util/StringUtilsTest.java new file mode 100644 index 00000000..4dd7fe87 --- /dev/null +++ b/src/test/java/org/dom4j/util/StringUtilsTest.java @@ -0,0 +1,45 @@ +package org.dom4j.util; + +import org.testng.annotations.Test; + +import static org.dom4j.util.StringUtils.endsWithWhitespace; +import static org.dom4j.util.StringUtils.startsWithWhitespace; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * Test of utility class StringUtils. + * + * @author Marián Petráš + */ +public class StringUtilsTest { + + @Test + public void testStartsWithWhitespace_empty() { + assertFalse(startsWithWhitespace("")); + } + + @Test + public void testStartsWithWhitespace_nonEmpty() { + assertTrue (startsWithWhitespace(" ")); + assertFalse(startsWithWhitespace("alpha")); + assertTrue (startsWithWhitespace(" alpha")); + assertFalse(startsWithWhitespace("alpha ")); + assertTrue (startsWithWhitespace(" alpha ")); + } + + @Test + public void testEndsWithWhitespace_empty() { + assertFalse(endsWithWhitespace("")); + } + + @Test + public void testEndsWithWhitespace_nonEmpty() { + assertTrue (endsWithWhitespace(" ")); + assertFalse(endsWithWhitespace("alpha")); + assertFalse(endsWithWhitespace(" alpha")); + assertTrue (endsWithWhitespace("alpha ")); + assertTrue (endsWithWhitespace(" alpha ")); + } + +}