8174025: Regression in XML Transform caused by JDK-8087303

Reviewed-by: joehw, dfuchs
This commit is contained in:
Frank Yuan 2017-02-15 11:43:23 +08:00
parent 51d9966f2d
commit 00af9d2013
18 changed files with 357 additions and 109 deletions

View File

@ -1385,7 +1385,7 @@ abstract public class ToStream extends SerializerBase {
return;
final boolean shouldNotFormat = !shouldFormatOutput();
if (m_elemContext.m_startTagOpen && shouldNotFormat)
if (m_elemContext.m_startTagOpen)
{
closeStartTag();
m_elemContext.m_startTagOpen = false;
@ -1411,8 +1411,12 @@ abstract public class ToStream extends SerializerBase {
if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
{
charactersRaw(chars, start, length);
m_isprevtext = true;
if (shouldNotFormat) {
charactersRaw(chars, start, length);
m_isprevtext = true;
} else {
m_charactersBuffer.addRawText(chars, start, length);
}
// time to fire off characters generation event
if (m_tracer != null)
super.fireCharEvent(chars, start, length);
@ -1420,7 +1424,7 @@ abstract public class ToStream extends SerializerBase {
return;
}
if (m_elemContext.m_startTagOpen && shouldNotFormat)
if (m_elemContext.m_startTagOpen)
{
closeStartTag();
m_elemContext.m_startTagOpen = false;
@ -1447,6 +1451,13 @@ abstract public class ToStream extends SerializerBase {
return m_doIndent && !m_ispreserveSpace;
}
/**
* @return True if the content in current element should be formatted.
*/
public boolean getIndent() {
return shouldFormatOutput();
}
/**
* Write out the characters.
*
@ -1550,12 +1561,7 @@ abstract public class ToStream extends SerializerBase {
*/
final protected void flushCharactersBuffer() throws SAXException {
try {
if (shouldFormatOutput() && m_charactersBuffer.hasContent()) {
if (m_elemContext.m_startTagOpen) {
closeStartTag();
m_elemContext.m_startTagOpen = false;
}
if (shouldFormatOutput() && m_charactersBuffer.isAnyCharactersBuffered()) {
if (m_elemContext.m_isCdataSection) {
/*
* due to cdata-section-elements atribute, we need this as
@ -1567,11 +1573,16 @@ abstract public class ToStream extends SerializerBase {
}
m_childNodeNum++;
boolean skipBeginningNewlines = false;
if (shouldIndentForText()) {
indent();
m_startNewLine = true;
// newline has always been added here because if this is the
// text before the first element, shouldIndent() won't
// return true.
skipBeginningNewlines = true;
}
m_charactersBuffer.flush();
m_charactersBuffer.flush(skipBeginningNewlines);
}
} catch (IOException e) {
throw new SAXException(e);
@ -2915,7 +2926,7 @@ abstract public class ToStream extends SerializerBase {
String value,
boolean xslAttribute)
{
if (m_charactersBuffer.isNoCharactersBuffered()) {
if (!m_charactersBuffer.isAnyCharactersBuffered()) {
return doAddAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
} else {
/*
@ -3396,15 +3407,16 @@ abstract public class ToStream extends SerializerBase {
*/
private abstract class GenericCharacters {
/**
* @return True if having any character other than whitespace or
* line feed.
* @return True if all characters in this Text are newlines.
*/
abstract boolean hasContent();
abstract void flush() throws SAXException;
abstract boolean flush(boolean skipBeginningNewlines) throws SAXException;
/**
* Converts this GenericCharacters to a new character array.
* Converts this GenericCharacters to a new character array. This
* method is used to handle cdata-section-elements attribute in
* xsl:output. Therefore it doesn't need to consider
* skipBeginningNewlines because the text will be involved with CDATA
* tag.
*/
abstract char[] toChars();
}
@ -3422,27 +3434,21 @@ abstract public class ToStream extends SerializerBase {
text = Arrays.copyOfRange(chars, start, start + length);
}
boolean hasContent() {
for (int i = 0; i < text.length; i++) {
if (!isWhiteSpace(text[i])) {
boolean flush(boolean skipBeginningNewlines) throws SAXException {
int start = 0;
while (skipBeginningNewlines && text[start] == '\n') {
start++;
if (start == text.length) {
return true;
}
}
outputCharacters(text, start, text.length - start);
return false;
}
void flush() throws SAXException {
outputCharacters(text, 0, text.length);
}
char[] toChars() {
return text;
}
boolean isWhiteSpace(char ch) {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}
});
}
@ -3451,12 +3457,22 @@ abstract public class ToStream extends SerializerBase {
*/
public void addEntityReference(String entityName) {
bufferedCharacters.add(new GenericCharacters() {
boolean hasContent() {
return true;
}
void flush() throws SAXException {
outputEntityReference(entityName);
boolean flush(boolean skipBeginningNewlines) throws SAXException {
if (m_elemContext.m_startTagOpen)
{
closeStartTag();
m_elemContext.m_startTagOpen = false;
}
if (m_cdataTagOpen)
closeCDATA();
char[] cs = toChars();
try {
m_writer.write(cs, 0, cs.length);
m_isprevtext = true;
} catch (IOException e) {
throw new SAXException(e);
}
return false;
}
char[] toChars() {
@ -3466,31 +3482,56 @@ abstract public class ToStream extends SerializerBase {
}
/**
* @return True if no GenericCharacters are buffered.
* Append a raw text to the buffer. Used to handle raw characters event.
*/
public boolean isNoCharactersBuffered() {
return bufferedCharacters.isEmpty();
public void addRawText(final char chars[], final int start, final int length) {
bufferedCharacters.add(new GenericCharacters() {
char[] text;
{
text = Arrays.copyOfRange(chars, start, start + length);
}
boolean flush(boolean skipBeginningNewlines) throws SAXException {
try {
int start = 0;
while (skipBeginningNewlines && text[start] == '\n') {
start++;
if (start == text.length) {
return true;
}
}
m_writer.write(text, start, text.length - start);
m_isprevtext = true;
} catch (IOException e) {
throw new SAXException(e);
}
return false;
}
char[] toChars() {
return text;
}
});
}
/**
* @return True if any buffered GenericCharacters has content.
* @return True if any GenericCharacters are buffered.
*/
public boolean hasContent() {
for (GenericCharacters element : bufferedCharacters) {
if (element.hasContent())
return true;
}
return false;
public boolean isAnyCharactersBuffered() {
return bufferedCharacters.size() > 0;
}
/**
* Flush all buffered GenericCharacters.
*/
public void flush() throws SAXException {
public void flush(boolean skipBeginningNewlines) throws SAXException {
Iterator<GenericCharacters> itr = bufferedCharacters.iterator();
boolean continueSkipBeginningNewlines = skipBeginningNewlines;
while (itr.hasNext()) {
GenericCharacters element = itr.next();
element.flush();
continueSkipBeginningNewlines = element.flush(continueSkipBeginningNewlines);
itr.remove();
}
}

View File

@ -1024,7 +1024,8 @@ final class DOM3TreeWalker {
return;
}
if (bDispatch) {
if (bDispatch
&& (!fSerializer.getIndent() || !node.getData().replace('\n', ' ').trim().isEmpty())) {
dispatachChars(node);
}
}

View File

@ -60,7 +60,7 @@ import org.xml.sax.SAXException;
/*
* @test
* @bug 6439439 8087303
* @bug 6439439 8087303 8174025
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng/othervm -DrunSecMngr=true common.prettyprint.PrettyPrintTest
* @run testng/othervm common.prettyprint.PrettyPrintTest
@ -69,29 +69,30 @@ import org.xml.sax.SAXException;
@Listeners({jaxp.library.FilePolicy.class})
public class PrettyPrintTest {
/*
* test CDATA, elements only, text and element, whitespace and element,
* xml:space property and nested xml:space property, mixed node types.
* test CDATA, elements only, text and element, xml:space property, mixed
* node types.
*/
@DataProvider(name = "xml-data")
public Object[][] xmlData() throws Exception {
return new Object[][] {
{ read("xmltest1.xml"), read("xmltest1.out") },
{ read("xmltest2.xml"), read("xmltest2.out") },
{ read("xmltest3.xml"), read("xmltest3.out") },
{ read("xmltest4.xml"), read("xmltest4.out") },
{ read("xmltest5.xml"), read("xmltest5.out") },
{ read("xmltest6.xml"), read("xmltest6.out") },
{ read("xmltest7.xml"), read("xmltest7.out") },
{ read("xmltest8.xml"), read("xmltest8.out") } };
{ "xmltest1.xml", "xmltest1.out" },
{ "xmltest2.xml", "xmltest2.out" },
{ "xmltest3.xml", "xmltest3.out" },
{ "xmltest4.xml", "xmltest4.out" },
{ "xmltest6.xml", "xmltest6.out" },
{ "xmltest8.xml", "xmltest8.out" } };
}
/*
* @bug 8087303
* Test the whitespace text nodes are serialized with pretty-print by LSSerializer and transformer correctly
* Test the xml document are serialized with pretty-print by
* LSSerializer and transformer correctly
*
*/
@Test(dataProvider = "xml-data")
public void testXMLPrettyPrint(String source, String expected) throws Exception {
public void testXMLPrettyPrint(String sourceFile, String expectedFile) throws Exception {
String source = read(sourceFile);
String expected = read(expectedFile);
// test it's no change if no pretty-print
String result = serializerWrite(toXmlDocument(source), false);
assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result);
@ -104,40 +105,116 @@ public class PrettyPrintTest {
assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected);
}
/*
* test pure text content, and sequent Text nodes.
* @bug 8087303
* Test a single text node is serialized with pretty-print by
* LSSerializer and transformer correctly
*
*/
@DataProvider(name = "xml-node-data")
public Object[][] xmlNodeData() throws Exception {
return new Object[][] {
{ newTextNode(read("nodetest1.txt")), read("nodetest1.out") },
{ createDocWithSequentTextNodes(), read("nodetest2.out") } };
@Test
public void testSingleTextNode() throws Exception {
Node xml = newTextNode(read("nodetest1.txt"));
String expected = read("nodetest1.out");
assertEquals(serializerWrite(xml, true), expected);
assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected);
}
/*
* @bug 8087303
* Test the whitespace text nodes are serialized with pretty-print by LSSerializer and transformer correctly,
* doesn't compare with the source because the test data is Node object
* Test the transformer shall keep all whitespace text node in
* sequent text nodes
*
*/
@Test(dataProvider = "xml-node-data")
public void testXMLNodePrettyPrint(Node xml, String expected) throws Exception {
assertEquals(serializerWrite(xml, true), expected);
@Test
public void testSequentTextNodesWithTransformer() throws Exception {
Node xml = createDocWithSequentTextNodes();
String expected = read("nodetest2.out");
assertEquals(transform(xml, true).replaceAll("\r\n", "\n"), expected);
}
/*
* @bug 8087303
* Test LSSerializer shall eliminate the whitespace text node
* in sequent text nodes
*
*/
@Test
public void testSequentTextNodesWithLSSerializer() throws Exception {
Node xml = createDocWithSequentTextNodes();
String expected = read("nodetest2ls.out");
assertEquals(serializerWrite(xml, true), expected);
}
/*
* test whitespace and element, nested xml:space property.
*/
@DataProvider(name = "xml-data-whitespace-ls")
public Object[][] whitespaceLS() throws Exception {
return new Object[][] {
{ "xmltest5.xml", "xmltest5ls.out" },
{ "xmltest7.xml", "xmltest7ls.out" } };
}
/*
* @bug 8087303
* Test LSSerializer shall eliminate the whitespace text node
* unless xml:space="preserve"
*
*/
@Test(dataProvider = "xml-data-whitespace-ls")
public void testWhitespaceWithLSSerializer(String sourceFile, String expectedFile) throws Exception {
String source = read(sourceFile);
String expected = read(expectedFile);
// test it's no change if no pretty-print
String result = serializerWrite(toXmlDocument(source), false);
assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result);
// test pretty-print
assertEquals(serializerWrite(toXmlDocument(source), true), expected);
}
/*
* test whitespace and element, nested xml:space property.
*/
@DataProvider(name = "xml-data-whitespace-xslt")
public Object[][] whitespaceXSLT() throws Exception {
return new Object[][] {
{ "xmltest5.xml", "xmltest5xslt.out" },
{ "xmltest7.xml", "xmltest7xslt.out" } };
}
/*
* @bug 8087303
* Test the transformer shall format the output but keep all
* whitespace text node even if xml:space="preserve"
*
*/
@Test(dataProvider = "xml-data-whitespace-xslt")
public void testWhitespaceWithTransformer(String sourceFile, String expectedFile) throws Exception {
String source = read(sourceFile);
String expected = read(expectedFile);
// test it's no change if no pretty-print
String result = transform(toXmlDocument(source), false);
assertTrue(toXmlDocument(source).isEqualNode(toXmlDocument(result)), "The actual is: " + result);
// test pretty-print
assertEquals(transform(toXmlDocument(source), true).replaceAll("\r\n", "\n"), expected);
}
/*
* test block element, inline element, text, and mixed elements.
*/
@DataProvider(name = "html-data")
public Object[][] htmlData() throws Exception {
return new Object[][] {
{ read("htmltest1.xml"), read("htmltest1.out") },
{ read("htmltest2.xml"), read("htmltest2.out") },
{ read("htmltest3.xml"), read("htmltest3.out") },
{ read("htmltest4.xml"), read("htmltest4.out") },
{ read("htmltest5.xml"), read("htmltest5.out") },
{ read("htmltest6.xml"), read("htmltest6.out") } };
{ "htmltest1.xml", "htmltest1.out" },
{ "htmltest2.xml", "htmltest2.out" },
{ "htmltest3.xml", "htmltest3.out" },
{ "htmltest4.xml", "htmltest4.out" },
{ "htmltest5.xml", "htmltest5.out" },
{ "htmltest6.xml", "htmltest6.out" },
/* @bug 8174025, test whitespace between inline elements */
{ "htmltest7.xml", "htmltest7.out" } };
}
/*
@ -146,7 +223,9 @@ public class PrettyPrintTest {
*
*/
@Test(dataProvider = "html-data")
public void testTransformToHTML(String source, String expected) throws Exception {
public void testTransformToHTML(String sourceFile, String expectedFile) throws Exception {
String source = read(sourceFile);
String expected = read(expectedFile);
// test it's no change if no pretty-print
StringWriter writer = new StringWriter();
getTransformer(true, false).transform(new StreamSource(new StringReader(source)), new StreamResult(writer));
@ -158,6 +237,27 @@ public class PrettyPrintTest {
assertEquals(writer.toString().replaceAll("\r\n", "\n"), expected);
}
/*
* @bug 8174025
* Test the serializer can handle <xsl:text disable-output-escaping="yes"> correctly.
*
*/
@Test
public void testDisableOutputEscaping() throws Exception {
final String xsl ="generate-catalog.xsl";
final String xml ="simple-entity-resolver-config.xml";
final String expectedOutput ="simple-entity-resolver-config-transformed.xml";
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTemplates(new StreamSource(new StringReader(read(xsl)))).newTransformer();
String key = "schemaBase";
String value = "schemas";
transformer.setParameter(key, value);
StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(new StringReader(read(xml))), new StreamResult(writer));
assertEquals(writer.toString().replaceAll("\r\n", "\n"), read(expectedOutput));
}
@Test
public void testLSSerializerFormatPrettyPrint() {
@ -298,6 +398,9 @@ public class PrettyPrintTest {
Document doc = db.newDocument();
Node root = doc.createElement("root");
doc.appendChild(root);
root.appendChild(doc.createTextNode("\n"));
root.appendChild(doc.createTextNode("\n"));
root.appendChild(doc.createTextNode("\n"));
root.appendChild(doc.createTextNode(" "));
root.appendChild(doc.createTextNode("t"));
root.appendChild(doc.createTextNode("\n"));

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Stylesheet for generating the entity-resolver document in XCatalog format -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="schemaBase"/>
<xsl:template match="entity-resolver-config">
<catalog xmlns="xmlns:xml:catalog"
prefer="system"
xml:base="{$schemaBase}" >
<xsl:for-each select="entity">
<!-- Generate System Id -->
<xsl:text disable-output-escaping="yes">&lt;system systemId="</xsl:text>
<xsl:value-of select="system-id/text()"/>
<xsl:text>" uri="</xsl:text>
<xsl:value-of select="location/text()"/>
<xsl:text disable-output-escaping="yes">" /&gt;&#10;</xsl:text>
</xsl:for-each>
</catalog>
</xsl:template>
</xsl:stylesheet>

View File

@ -1 +1 @@
<rss version="2.0"><channel xml:space="preserve"><title>Java Tutorials and Examples 1</title> <language>en-us</language></channel></rss>
<rss version="2.0"><channel xml:space="preserve"><title>Java Tutorials and Examples 1</title><language>en-us</language></channel></rss>

View File

@ -0,0 +1,7 @@
<html>
<body>
<p>
<span>this</span> <span>is</span> <span>a</span> <span>whitespace</span> <span>inline element</span> <span>test</span>
</p>
</body>
</html>

View File

@ -0,0 +1 @@
<html><body><p> <span>this</span> <span>is</span> <span>a</span> <span>whitespace</span> <span>inline element</span> <span>test</span> </p></body></html>

View File

@ -1,19 +1,30 @@
<root>
t
t
<child1/>
<child1>
</child1>
t
<child2/>
<child3/>
<child4/>
<child2> </child2>
<child3> </child3>
<child4> </child4>
<child5>
t
<child51>
<child511>t</child511>
</child51>
t
</child5>
<!-- test comment -->
<!-- -->
<?target1 test?>
</root>

View File

@ -0,0 +1,18 @@
<root>
tt
<child1/>
t
<child2/>
<child3/>
<child4/>
<child5>
t
<child51>
<child511>t</child511>
</child51>
t
</child5>
<!-- test comment -->
<!-- -->
<?target1 test?>
</root>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?><catalog prefer="system" xml:base="schemas" xmlns="xmlns:xml:catalog"><system systemId="http://www.example.test/oracle/schema/example1.xsd" uri="META-INF/example1.xsd" />
<system systemId="http://www.example.test/oracle/schema/example2.xsd" uri="META-INF/example2.xsd" />
</catalog>

View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<entity-resolver-config
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.example.test/schema.xsd"
schema-major-version="1"
schema-minor-version="1">
<entity>
<description>Example 1 Schema Type library 10.0 </description>
<public-id>>-//Oracle//Example 1 Schema Type library 10.0//EN</public-id>
<system-id>http://www.example.test/oracle/schema/example1.xsd</system-id>
<location>META-INF/example1.xsd</location>
</entity>
<entity>
<description>Example 2 Schema Type library 10.0 </description>
<public-id>>-//Oracle//Example 2 Schema Type library 10.0//EN</public-id>
<system-id>http://www.example.test/oracle/schema/example2.xsd</system-id>
<location>META-INF/example2.xsd</location>
</entity>
</entity-resolver-config>

View File

@ -0,0 +1,15 @@
<rss version="2.0">
<channel>
<title>Java Tutorials and Examples 1</title>
<language>en-us</language>
</channel>
<a>
<b> </b>
</a>
<c>
</c>
</rss>

View File

@ -0,0 +1,17 @@
<rss>
<layer1 xml:space="preserve"> <title>Java </title> <layer2 xml:space="asfsa"> <layer3> <layer4 xml:space="default">
<l5>5</l5>
</layer4> </layer3> </layer2> <layer2 xml:space="default">
<layer3>
<l4> </l4>
</layer3>
<layer3 xml:space="preserve"> <l4> </l4> </layer3>
</layer2> </layer1>
</rss>

View File

@ -1,25 +1,20 @@
<root>
t
t
<![CDATA[ ]]>
t
t
<child1/>
t
t
<!-- test comment -->
<child2/>
<child5>
t
t
<?target1 test?>
<child51>
<child511>t</child511>
</child51>
<?target1 test?>
t
t
</child5>
</root>

View File

@ -2,14 +2,7 @@
t<![CDATA[ ]]>
t
<child1/>
t<!-- test comment -->
<child2/>
<child5>
t<?target1 test?>
<child51>
<child511>t</child511>
</child51><?target1 test?>
t<!-- test comment --><child2/><child5>
t<?target1 test?><child51><child511>t</child511></child51><?target1 test?>
t
</child5>
</root>
</child5></root>

View File

@ -279,11 +279,11 @@ public class LSSerializerTest {
"<author>\n" +
" <a>&name1;Jo Smith</a>\n" +
" <b>b &name2;Jo Smith &name1;Jo Smith b</b>\n" +
" <c> &name;Jo Smith </c>\n" +
" <c>&name;Jo Smith </c>\n" +
" <d>&ele1;d</d>\n" +
" <e> &ele2;eee </e>\n" +
" <e>&ele2;eee </e>\n" +
" <f>&lt;att&gt;</f>\n" +
" <g> &ele; g</g>\n" +
" <g>&ele; g</g>\n" +
" <h>&ele2;</h>\n" +
"</author>\n");
@ -301,7 +301,7 @@ public class LSSerializerTest {
"<author>\n" +
" <a>&name;Jo Smith</a>\n" +
" <b>b &name;Jo Smith &name;Jo Smith b</b>\n" +
" <c> &name;Jo Smith </c>\n" +
" <c>&name;Jo Smith </c>\n" +
" <d>\n" +
" <aa>\n" +
" <bb>text</bb>\n" +