@@ -125,24 +125,53 @@ public final class XMLParserTest extends SandboxTest {
125125
126126 /** Internal and default parser must agree on DTD content models. */
127127 @ Test public void dtdContentModelParsers () {
128- set (MainOptions .DTD , true );
129-
130128 // well-formed documents with an internal subset and a matching element tree
131- final String [] docs = {
129+ sameOnBothParsers (
132130 "<!DOCTYPE a [ <!ELEMENT a (b, c)> ]><a><b/><c/></a>" ,
133131 "<!DOCTYPE a [ <!ELEMENT a ((b)?, (c)*)> ]><a><c/><c/></a>" ,
134132 "<!DOCTYPE a [ <!ELEMENT a (b | c)> ]><a><b/></a>" ,
135133 "<!DOCTYPE a [ <!ELEMENT a (#PCDATA | b)*> ]><a>x<b/>y</a>" ,
136- "<!DOCTYPE a [ <!ELEMENT a (b, (c, d)+, e)> ]><a><b/><c/><d/><e/></a>" ,
137- };
138- for (final String doc : docs ) {
139- set (MainOptions .INTPARSE , false );
140- execute (new CreateDB (NAME , doc ));
141- final String def = query ("." );
142- set (MainOptions .INTPARSE , true );
143- execute (new CreateDB (NAME , doc ));
144- assertEquals (def , query ("." ), "Parsers disagree on: " + doc );
145- }
134+ "<!DOCTYPE a [ <!ELEMENT a (b, (c, d)+, e)> ]><a><b/><c/><d/><e/></a>" );
135+ }
136+
137+ /**
138+ * Internal and default parser must agree on attribute defaults (incl. #FIXED and enumeration
139+ * defaults) and on tokenized-type attribute-value normalization.
140+ */
141+ @ Test public void dtdAttributes () {
142+ sameOnBothParsers (
143+ // default values, incl. empty and multiple defaults; specified values win
144+ "<!DOCTYPE a [ <!ATTLIST a b CDATA \" x\" > ]><a/>" ,
145+ "<!DOCTYPE a [ <!ATTLIST a b CDATA \" x\" > ]><a b=\" y\" />" ,
146+ "<!DOCTYPE a [ <!ATTLIST a b CDATA \" \" > ]><a/>" ,
147+ "<!DOCTYPE a [ <!ATTLIST a b CDATA \" 1\" c CDATA \" 2\" > ]><a c=\" X\" />" ,
148+ // #FIXED and enumeration defaults
149+ "<!DOCTYPE a [ <!ATTLIST a b CDATA #FIXED \" x\" > ]><a/>" ,
150+ "<!DOCTYPE a [ <!ATTLIST a b (l | r) \" l\" > ]><a/>" ,
151+ // entity reference in a default value
152+ "<!DOCTYPE a [ <!ATTLIST a b CDATA \" A\" > ]><a/>" ,
153+ // tokenized-type normalization (collapse + trim); CDATA is left untouched
154+ "<!DOCTYPE a [ <!ATTLIST a b NMTOKEN #IMPLIED> ]><a b=\" x \" />" ,
155+ "<!DOCTYPE a [ <!ATTLIST a b NMTOKENS #IMPLIED> ]><a b=\" x y \" />" ,
156+ "<!DOCTYPE a [ <!ATTLIST a b (l | r) #IMPLIED> ]><a b=\" l \" />" ,
157+ "<!DOCTYPE a [ <!ATTLIST a b CDATA #IMPLIED> ]><a b=\" x y \" />" ,
158+ // #IMPLIED / #REQUIRED add nothing
159+ "<!DOCTYPE a [ <!ATTLIST a b CDATA #IMPLIED> ]><a/>" ,
160+ "<!DOCTYPE a [ <!ATTLIST a b CDATA #REQUIRED> ]><a b=\" z\" />" );
161+ }
162+
163+ /** Internal and default parser must agree on ignorable (element-content) whitespace. */
164+ @ Test public void dtdElementContentWhitespace () {
165+ sameOnBothParsers (
166+ // element-only content: whitespace between children is ignorable and dropped
167+ "<!DOCTYPE a [ <!ELEMENT a (b, b)><!ELEMENT b EMPTY> ]><a>\n <b/>\n <b/>\n </a>" ,
168+ "<!DOCTYPE a [ <!ELEMENT a (b)><!ELEMENT b (c)><!ELEMENT c EMPTY> ]>" +
169+ "<a>\n <b>\n <c/>\n </b>\n </a>" ,
170+ // mixed content and ANY: whitespace is significant and kept
171+ "<!DOCTYPE a [ <!ELEMENT a (#PCDATA | b)*><!ELEMENT b EMPTY> ]><a>\n <b/>\n </a>" ,
172+ "<!DOCTYPE a [ <!ELEMENT a ANY><!ELEMENT b EMPTY> ]><a>\n <b/>\n </a>" ,
173+ // non-whitespace text in element content is kept by both (non-validating)
174+ "<!DOCTYPE a [ <!ELEMENT a (b)><!ELEMENT b EMPTY> ]><a> x <b/> y </a>" );
146175 }
147176
148177 /** A malformed DTD must report its real cause, not a masked "empty document" error. */
@@ -161,26 +190,6 @@ public final class XMLParserTest extends SandboxTest {
161190 assertTrue (empty .contains ("No input found" ), "Unexpected error: " + empty );
162191 }
163192
164- /** Internal parser: ATTLIST declarations, including enumerations and empty default values. */
165- @ Test public void dtdAttlist () {
166- set (MainOptions .INTPARSE , true );
167- set (MainOptions .DTD , true );
168-
169- // attribute declarations that must be accepted; an empty default value ("") regressed the
170- // scanner before the fix, overrunning the whole declaration
171- final String [] attlists = {
172- "<!ATTLIST a b CDATA \" \" >" ,
173- "<!ATTLIST a b CDATA '' c CDATA '50'>" ,
174- "<!ATTLIST a b (x | y | z) \" x\" >" ,
175- "<!ATTLIST a b NMTOKEN #IMPLIED c CDATA #REQUIRED d CDATA #FIXED \" 1\" >" ,
176- "<!ATTLIST a align (left | right | center | justify | char) \" left\" char CDATA \" \" >" ,
177- };
178- for (final String attlist : attlists ) {
179- execute (new CreateDB (NAME , "<!DOCTYPE a [ " + attlist + " ]><a/>" ));
180- query ("." , "<a/>" );
181- }
182- }
183-
184193 /**
185194 * Internal parser: a complex external DTD subset (parameter entities inside declarations,
186195 * a parameter-entity-driven conditional section, enumerations and empty default values).
@@ -220,6 +229,22 @@ public final class XMLParserTest extends SandboxTest {
220229 query ("name(*)" , "tgroup" );
221230 }
222231
232+ /**
233+ * Asserts that the internal and the default parser produce identical documents.
234+ * @param docs document strings (each with an internal DTD subset)
235+ */
236+ private void sameOnBothParsers (final String ... docs ) {
237+ set (MainOptions .DTD , true );
238+ for (final String doc : docs ) {
239+ set (MainOptions .INTPARSE , false );
240+ execute (new CreateDB (NAME , doc ));
241+ final String def = query ("." );
242+ set (MainOptions .INTPARSE , true );
243+ execute (new CreateDB (NAME , doc ));
244+ assertEquals (def , query ("." ), "Parsers disagree on: " + doc );
245+ }
246+ }
247+
223248 /**
224249 * Creates a database and returns the resulting error message, or {@code null} on success.
225250 * @param doc document string
0 commit comments