PE Reference splitting in INCLUDE-section

"Dr. Stefan Bettner" <[email protected]> Tue, 21 Sep 2021 19:48:58 +0200
Newsgroups gmane.text.xml.xerces-j.user
Message-ID <[email protected]>
Hi everybody,

I have a question regarding the Java xml parser. I have some behavior
that baffles me, regarding the replacement text of parameter entity
references within a conditional INCLUDE-section. And I would really be
grateful if someone could explain it to me.

When I look into the xml specification (fifth edition), they state:

"Well-formedness constraint: PE Between Declarations
The replacement text of a parameter entity reference in a DeclSep MUST
match the production extSubsetDecl."

That prevents for example to split a markup declaration into the
replacement text of two separate parameter entities. Like for example
the entity-declaration

<!ENTITY copyright '(C)'>

cannot be split into two parameter entity references like that:

<!ENTITY % A "<!ENTITY ">
<!ENTITY % B "copyright '(C)'>">
%A;%B;

because in that case the replacement text for %A; would not match the
production extSubsetDecl (as required), because it is incomplete.

And the Java xml-parser reports the above as a fatal error, in
validation and non-validation-mode alike. So far so good.

But strangely the situation changes when the expression %A;%B; is put
into a conditional INCLUDE-section, like that:

<!ENTITY % A "<!ENTITY ">
<!ENTITY % B "copyright '(C)'>">
<![INCLUDE[%A;%B;]]>

In that case the Java xml-parser has no problem at all with the above in
non-validation-mode, and in validation-mode only two validation errors
are given, but no fatal error. In both cases the entity "copyright" is
declared and can be used within the document.

How can that be? I would really be grateful if someone could explain
that to me.

When I look at the grammatical definition of a conditional include-section

[62]=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 includeSect=C2=A0=C2=A0=C2=A0 =C2=A0=
=C2=A0 ::=3D=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 '<![' S? 'INCLUDE' S? '['
extSubsetDecl ']]>'

the inner part should be an extSubsetDecl. So with looking at

[31]=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 extSubsetDecl=C2=A0=C2=A0=C2=A0 =C2=A0=
=C2=A0 ::=3D=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 ( markupdecl | conditionalSect
| DeclSep)*

in our case, when processing the INCLUDE-section, %A; can only match a
DeclSep, and with the well-formedness constraint meantioned above, I
would have assumed that the replacement text of %A; "MUST match the
production extSubsetDecl". But it does not, since the replacement text
of %A; is incomplete.

I would be grateful for any hint.
Thank you so much for your work.
Apache is such a great project.

Bye everybody.
Stay healthy you all.

Stefan Bettner.


PS: I appended my xml files and Java file, for comparison. I was using
the following Java-version on Windows 10:
java version "12.0.2" 2019-07-16
Java(TM) SE Runtime Environment (build 12.0.2+10)

splitEntity.xml

<?xml version=3D"1.0" encoding=3D"UTF-8"?>
<!DOCTYPE Root SYSTEM "splitEntity.dtd" [
<!ELEMENT Root ANY>
] >
<Root>&copyright;</Root>


splitEntity.dtd

<!ENTITY % A "<!ENTITY ">
<!ENTITY % B "copyright '(C)'>">
<![INCLUDE[%A;%B;]]>


XMLSplitEntity.java

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;

public class XMLSplitEntity
 =C2=A0 {
 =C2=A0 /*
 =C2=A0=C2=A0 * custom DocumentHandler
 =C2=A0=C2=A0 */
 =C2=A0 public static class MyDocumentHandler extends DefaultHandler
 =C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0 @Override
 =C2=A0=C2=A0=C2=A0 public void characters (char[] ch, int start, int leng=
th) throws
SAXException
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 System.out.println ("characters: " + new S=
tring (ch, start, length));
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }

 =C2=A0=C2=A0=C2=A0 @Override
 =C2=A0=C2=A0=C2=A0 public void warning (SAXParseException e) throws SAXEx=
ception
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 System.out.println ("warning: " + e.getMes=
sage ());
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }

 =C2=A0=C2=A0=C2=A0 @Override
 =C2=A0=C2=A0=C2=A0 public void error (SAXParseException e) throws SAXExce=
ption
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 System.out.println ("error: " + e.getMessa=
ge ());
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }

 =C2=A0=C2=A0=C2=A0 @Override
 =C2=A0=C2=A0=C2=A0 public void fatalError (SAXParseException e) throws SA=
XException
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 System.out.println ("fatalError: " + e.get=
Message ());
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 }

 =C2=A0 /*
 =C2=A0=C2=A0 * parse splitEntity.xml
 =C2=A0=C2=A0 * (with external splitEntity.dtd)
 =C2=A0=C2=A0 */
 =C2=A0 public static void main (String[] args)
 =C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0 // create parser
 =C2=A0=C2=A0=C2=A0 SAXParserFactory factory =3D SAXParserFactory.newInsta=
nce ();
 =C2=A0=C2=A0=C2=A0 // factory.setValidating (true);
 =C2=A0=C2=A0=C2=A0 SAXParser saxParser;
 =C2=A0=C2=A0=C2=A0 try
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 saxParser =3D factory.newSAXParser ();
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 catch (ParserConfigurationException | SAXException e)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 e.printStackTrace ();
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return;
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }

 =C2=A0=C2=A0=C2=A0 // parse
 =C2=A0=C2=A0=C2=A0 File file =3D new File ("C:\\splitEntity.xml");
 =C2=A0=C2=A0=C2=A0 try
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 saxParser.parse (file, new MyDocumentHandl=
er ());
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 catch (SAXException | IOException e)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 e.printStackTrace ();
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
 =C2=A0=C2=A0=C2=A0 }
 =C2=A0 }