Preserving doctype Information

Preserving doctype Information

This section describes the concepts behind preserving doctype information. For the resources associated with this sample, see Bitbucket.

While the PipelinedComparator and DXP pipelines have facilities for specifying a doctype using output properties, they used fixed values, or at best, values that must be specified as parameters. If the input files themselves contain a doctype, it is often preferable to use this as the doctype for the output document without having to specify it. This sample pipeline demonstrates how to output a dynamic doctype based on the doctypes in the input documents.

Note that it may be easier to select one of the pre-configured lexical preservation modes as discussed in the Lexical Preservation Concepts, as many of them include the dynamic preservation of doctypes.

Simple API Approach

A simple approach for retaining doctypes is to enable the built-in lexical preservation on our comparators, such as the PipelinedComparator, which are configured by passing them a LexicalPreservationConfig object. The following code extract illustrates how to enable just doctype preservation on a LexicalPreservationConfig object.

Java
LexicalPreservationConfig lpc = new LexicalPreservationConfig("base");
lpc.setPreserveDoctype(true);

Having enabled the preservation the next step is to specify how changes in doctype and its optional internal subset should be handled. It is straightforward to handle unchanged doctypes — their input value is passed through to the output, possibly with different whitespace layout as this is not reported by the parser. The difficulty comes in working out how to handle inconsistent doctypes, as it is not feasible for an output XML document to have more than one doctype. Here, an answer could be to choose one of the input doctypes, and hope that they are compatible. The 'B' input can be chosen as follows:

Java
lpc.setDoctypeProcessingMode(PreservationProcessingMode.B);

One problem with the above 'input selection' approach is that a doctype's internal subset can declare elements, attributes and entity references, which are used in the document. Therefore, removing these declarations could cause the output document to become invalid. Hence, the lexical preservation scheme provides a special output mode that enables the internal subset declarations from both inputs to be kept, except where they conflict, in which case one is chosen. Setting the doctype output mode to 'BdA' chooses the 'B' version of all doctype information, and the 'A' version of any declarations when there is no 'B' version of it.

Java
lpc.setDoctypeProcessingMode(PreservationProcessingMode.BdA);

DCP or DXP Approach

You can specify the lexical preservation options in a DXP (for Pipelined Comparator) or DCP (for Document Comparator) configuration file using the lexicalPreservation element. For example, the following DCP file can be used to set up the lexical preservation configuration in the same manner as that discussed in the Simple API approach above.

XML
<documentComparator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="1.0"
    xsi:noNamespaceSchemaLocation="../dcp/core-dcp-v1_0.xsd"
    id="preserve-doctype"
    description="Render result as folding html view." >
  <standardConfig>
    <lexicalPreservation>
      <defaults>
        <retain literalValue="false"/>
      </defaults>
      <overrides>
        <preserveItems>
          <doctype>
            <retain literalValue="true"/>
            <processingMode literalValue="BdA"/>
          </doctype>
        </preserveItems>
      </overrides>
    </lexicalPreservation>
  </standardConfig>
</documentComparator>

Running the Sample

See the sample resources and a description on how to run it here.

The resources must be checked out, cloned or downloaded and unzipped into the samples directory of the XML Compare release. They should be located such that they are two levels below the top level release directory, for example DeltaNova-year.x.y.z_j/samples/preserving-doctype-information.

Explanation

Converting a doctype into XML

The first step that must be performed is to convert the doctype into XML inside the document. As it stands, the doctype is not technically part of the XML document itself and will not pass through from the parser to the Comparator unless we intervene. In order to preserve this information, the Pipelined Comparator and Document Comparator use a built-in filter — the first filter in the input pipeline. The purpose of this filter, amongst other things, is to convert the reported doctype information into an element that is added as the first child of the root element.

Example 1: input document with doctype

XML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [
  <!ELEMENT testDelete (EMPTY)>
  <!ELEMENT testCommon (EMPTY)>
  <!ELEMENT testConflict (EMPTY)>
]>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head><title>A xhtml document</title></head>
  <body><p>A xhtml document.</p></body>
</html>

Example 2: the input document after passing through the filter used for lexical preservation

XML
<html xmlns:preserve="http://www.deltaxml.com/ns/preserve"
      xmlns="http://www.w3.org/1999/xhtml">
  <preserve:doctype name="html"
    publicId="-//W3C//DTD XHTML 1.0 Transitional//EN"
    systemId="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <preserve:elementDecl name="testDelete" deltaxml:key="element_testDelete" model="(EMPTY)"/>
    <preserve:elementDecl name="testCommon" deltaxml:key="element_testCommon" model="(EMPTY)"/>
    <preserve:elementDecl name="testConflict" deltaxml:key="element_testConflict" model="(EMPTY)"/>
  </preserve:doctype>
  <head><title>A xhtml document</title></head>
  <body><p>A xhtml document.</p></body>
</html>

Handling Changes to the doctype

Now that the doctype is held as XML within the document, it will be compared as part of the comparison process. This means that if the input documents have different doctypes, that change will be reflected in the result document. In order to output the doctype correctly, we must decide which version is going to be used. An instance of the Core S9API LexicalPreservationConfig class can be used to choose which version of the doctype to output in the event of a change. For more information on the ignoring changes, see Ignoring Changes in a Comparison.

Example 3 shows how a doctype may change. The XML shown is a snippet from the immediate output of the comparator when comparing input1.xhtml and input2.xhtml (included in Bitbucket, https://bitbucket.org/deltaxml/preserving-doctype-information )

Example 3: a modified <preserve:doctype> element

XML
<html xmlns:deltaxml="http://www.deltaxml.com/ns/well-formed-delta-v1"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:dxa="http://www.deltaxml.com/ns/non-namespaced-attribute"
      xmlns:preserve="http://www.deltaxml.com/ns/preserve"
      deltaxml:deltaV2="A!=B" deltaxml:version="2.0" 
      deltaxml:content-type="full-context">
   <preserve:doctype deltaxml:deltaV2="A!=B" name="html">
      <deltaxml:attributes deltaxml:deltaV2="A!=B">
         <dxa:publicId deltaxml:deltaV2="A!=B">
            <deltaxml:attributeValue deltaxml:deltaV2="A">
              -//W3C//DTD XHTML 1.0 Transitional//EN
            </deltaxml:attributeValue>
            <deltaxml:attributeValue deltaxml:deltaV2="B">
              -//W3C//DTD XHTML 1.0 Strict//EN
            </deltaxml:attributeValue>
         </dxa:publicId>
         <dxa:systemId deltaxml:deltaV2="A!=B">
            <deltaxml:attributeValue deltaxml:deltaV2="A">
              http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
            </deltaxml:attributeValue>
            <deltaxml:attributeValue deltaxml:deltaV2="B">
              http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
            </deltaxml:attributeValue>
         </dxa:systemId>
      </deltaxml:attributes>
      <preserve:elementDecl deltaxml:deltaV2="A" name="testDelete" 
        deltaxml:key="element_testDelete" model="(EMPTY)"/>
      <preserve:elementDecl deltaxml:deltaV2="A=B" name="testCommon"
        deltaxml:key="element_testCommon" model="(EMPTY)"/>
      <preserve:elementDecl deltaxml:deltaV2="B" name="testAdd"
        deltaxml:key="element_testAdd" model="(EMPTY)"/>
      <preserve:elementDecl deltaxml:deltaV2="A!=B" name="testConflict"
         deltaxml:key="element_testConflict">
         <deltaxml:attributes deltaxml:deltaV2="A!=B">
            <dxa:model deltaxml:deltaV2="A!=B">
               <deltaxml:attributeValue deltaxml:deltaV2="A">(EMPTY)</deltaxml:attributeValue>
               <deltaxml:attributeValue deltaxml:deltaV2="B">(ANY)</deltaxml:attributeValue>
            </dxa:model>
         </deltaxml:attributes>
      </preserve:elementDecl>
   </preserve:doctype>
   <head deltaxml:deltaV2="A=B">
      <title>A xhtml document</title>
   </head>
   <body deltaxml:deltaV2="A=B">
      <p>A xhtml document (view source for DOCTYPE and internal subset details).</p>
   </body>
</html>

As can be seen above, the publicId and systemId for the doctype have changed between the inputs. In order to output a doctype in the result we need to decide which version we are going to use. An instance of the 'Core S9API' LexicalPreservationConfig class can be used to choose which version of the doctype to output in the event of a change (as discussed previously). Example 4 shows the output when the DOCTYPE output mode is set to 'BdA'; i.e. to output the B doctype where present, or the A doctype if there wasn't one in input B.

Example 4: the resulting file

XML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
  <!ELEMENT testDelete (EMPTY)>
  <!ELEMENT testCommon (EMPTY)>
  <!ELEMENT testAdd (EMPTY)>
  <!ELEMENT testConflict (ANY)>
]>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:deltaxml="http://www.deltaxml.com/ns/well-formed-delta-v1"
      deltaxml:deltaV2="A=B" deltaxml:version="2.0" deltaxml:content-type="full-context">
  <head>
    <title>A xhtml document</title>
  </head>
  <body>
    <p>A xhtml document (view source for DOCTYPE and internal subset details).</p>
  </body>
</html>

Note the old testDelete element declaration was only in 'A' input and modified testConflict element declaration was in both the 'A' and 'B' inputs. Therefore, according to the 'BdA' behaviour the deleted declaration (i.e. testDelete) and the 'B' version of the modified declaration are in the output.