Difficulty: ★★☆ (medium)
Keywords: splitting, exsl:document

Problem

You have a big DocBook document, like a book, and you want to split each chapter, appendix etc. into a separate file.

Solution

There are three solutions to this problems:

The following subsections describe each method.

Splitting with XSLT

The stylesheet in Example 3.6, “dbsplit.xsl uses the extension element to create separate documents from the main output. It uses code from html/chunk-code.xsl.

Example 3.6. dbsplit.xsl
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet 
[
 <!ENTITY db "https://cdn.docbook.org/release/xsl/current"> 
]>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:d="http://docbook.org/ns/docbook"
  xmlns:exsl="http://exslt.org/common"
  xmlns:xi="http://www.w3.org/2001/XInclude"
  extension-element-prefixes="exsl">

  <xsl:import href="chunker.xsl"/>
  <xsl:import href="copy.xsl"/>
  <xsl:output indent="yes"/>


  <xsl:param name="base.dir" select="'out/'"/>
  <xsl:param name="use.id.as.filename" select="0"/>
  <xsl:param name="rootid"/>

  <xsl:param name="dbsplit.root.filename">Index</xsl:param>
  <xsl:param name="dbsplit.ext">.xml</xsl:param>
  <xsl:param name="dbsplit.chunk.depth" select="2"/>

  <xsl:template name="object.id">
    <xsl:param name="object" select="."/>
    <xsl:choose>
      <xsl:when test="$object/@id">
        <xsl:value-of select="$object/@id"/>
      </xsl:when>
      <xsl:when test="$object/@xml:id">
        <xsl:value-of select="$object/@xml:id"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="generate-id($object)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="*" mode="recursive-chunk-filename">
    <xsl:param name="recursive" select="false()"/>
    <xsl:variable name="filename">
      <xsl:choose>
        <!-- if this is the root element, use the dbsplit.root.filename -->
        <xsl:when test="not(parent::*) and $dbsplit.root.filename != ''">
          <xsl:value-of select="$dbsplit.root.filename"/>
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:when>
        <!-- Special case -->
        <xsl:when
          test="self::d:legalnotice and not($generate.legalnotice.link = 0)">
          <xsl:choose>
            <xsl:when
              test="(@id or @xml:id) and not($use.id.as.filename = 0)">
              <!-- * if this legalnotice has an ID, then go ahead and use -->
              <!-- * just the value of that ID as the basename for the file -->
              <!-- * (that is, without prepending an "ln-" too it) -->
              <xsl:value-of select="(@id|@xml:id)[1]"/>
              <xsl:value-of select="$dbsplit.ext"/>
            </xsl:when>
            <xsl:otherwise>
              <!-- * otherwise, if this legalnotice does not have an ID, -->
              <!-- * then we generate an ID... -->
              <xsl:variable name="id">
                <xsl:call-template name="object.id"/>
              </xsl:variable>
              <!-- * ...and then we take that generated ID, prepend an -->
              <!-- * "ln-" to it, and use that as the basename for the file -->
              <xsl:value-of select="concat('ln-',$id,$dbsplit.ext)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <!-- if there's no dbhtml filename, and if we're to use IDs as -->
        <!-- filenames, then use the ID to generate the filename. -->
        <xsl:when test="(@id or @xml:id) and $use.id.as.filename != 0">
          <xsl:value-of select="(@id|@xml:id)[1]"/>
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:variable>

    <xsl:choose>
      <xsl:when test="not($recursive) and $filename != ''">
        <!-- if this chunk has an explicit name, use it -->
        <xsl:value-of select="$filename"/>
      </xsl:when>

      <xsl:when test="self::d:set">
        <xsl:value-of select="$dbsplit.root.filename"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:book">
        <xsl:text>bk</xsl:text>
        <xsl:number level="any" format="01"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:article">
        <xsl:if test="/d:set">
          <!-- in a set, make sure we inherit the right book info... -->
          <xsl:apply-templates mode="recursive-chunk-filename"
            select="parent::*">
            <xsl:with-param name="recursive" select="true()"/>
          </xsl:apply-templates>
        </xsl:if>

        <xsl:text>ar</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:preface">
        <xsl:if test="/d:set">
          <!-- in a set, make sure we inherit the right book info... -->
          <xsl:apply-templates mode="recursive-chunk-filename"
            select="parent::*">
            <xsl:with-param name="recursive" select="true()"/>
          </xsl:apply-templates>
        </xsl:if>

        <xsl:text>pr</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:chapter">
        <xsl:if test="/d:set">
          <!-- in a set, make sure we inherit the right book info... -->
          <xsl:apply-templates mode="recursive-chunk-filename"
            select="parent::*">
            <xsl:with-param name="recursive" select="true()"/>
          </xsl:apply-templates>
        </xsl:if>

        <xsl:text>ch</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:appendix">
        <xsl:if test="/d:set">
          <!-- in a set, make sure we inherit the right book info... -->
          <xsl:apply-templates mode="recursive-chunk-filename"
            select="parent::*">
            <xsl:with-param name="recursive" select="true()"/>
          </xsl:apply-templates>
        </xsl:if>

        <xsl:text>ap</xsl:text>
        <xsl:number level="any" format="a" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:part">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>pt</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:reference">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>rn</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:refentry">
        <xsl:choose>
          <xsl:when test="parent::d:reference">
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise>
            <xsl:if test="/d:set">
              <!-- in a set, make sure we inherit the right book info... -->
              <xsl:apply-templates mode="recursive-chunk-filename"
                select="parent::*">
                <xsl:with-param name="recursive" select="true()"/>
              </xsl:apply-templates>
            </xsl:if>
          </xsl:otherwise>
        </xsl:choose>

        <xsl:text>re</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:colophon">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>co</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:sect1 or self::d:sect2 or self::d:sect3 or 
                      self::d:sect4 or self::d:sect5 or self::d:section">
        <xsl:apply-templates mode="recursive-chunk-filename"
          select="parent::*">
          <xsl:with-param name="recursive" select="true()"/>
        </xsl:apply-templates>
        <xsl:text>s</xsl:text>
        <xsl:number format="01"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:bibliography">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>bi</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:glossary">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>go</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:index">
        <xsl:choose>
          <xsl:when test="/d:set">
            <!-- in a set, make sure we inherit the right book info... -->
            <xsl:apply-templates mode="recursive-chunk-filename"
              select="parent::*">
              <xsl:with-param name="recursive" select="true()"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise> </xsl:otherwise>
        </xsl:choose>

        <xsl:text>ix</xsl:text>
        <xsl:number level="any" format="01" from="d:book"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:when test="self::d:setindex">
        <xsl:text>si</xsl:text>
        <xsl:number level="any" format="01" from="d:set"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:when>

      <xsl:otherwise>
        <xsl:text>chunk-filename-error-</xsl:text>
        <xsl:value-of select="name(.)"/>
        <xsl:number level="any" format="01" from="d:set"/>
        <xsl:if test="not($recursive)">
          <xsl:value-of select="$dbsplit.ext"/>
        </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="generate-filename">
    <xsl:apply-templates select="." mode="recursive-chunk-filename"/>
  </xsl:template>

  <xsl:template name="generate-content">
    <xsl:variable name="filename">
      <xsl:call-template name="make-relative-filename">
        <xsl:with-param name="base.dir" select="$base.dir"/>
        <xsl:with-param name="base.name">
           <xsl:call-template name="generate-filename"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="depth" select="count(ancestor::*)"/>
    
    <!--<xsl:message>generate-content:
      name: <xsl:value-of select="name()"/>
      filename: <xsl:value-of select="$filename"/>
      depth: <xsl:value-of select="$depth"/>
      test: <xsl:value-of select="$depth >= $dbsplit.chunk.depth"/>
    </xsl:message>-->
    
    <xsl:choose>
      <xsl:when test="$depth &lt;= $dbsplit.chunk.depth">
        <xi:include href="{$filename}"/>
        <xsl:call-template name="write.xml.chunk">
          <xsl:with-param name="filename" select="$filename"/>
          <xsl:with-param name="content">
            <xsl:copy-of select="preceding-sibling::processing-instruction()|
                                 preceding-sibling::comment()"/>
            <xsl:copy>
              <xsl:apply-templates/>
            </xsl:copy>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template
    match="d:acknowledgements|d:appendix|d:article|
           d:bibliography|
           d:chapter|d:colophon|d:dedication|d:glossary|
           d:part|d:preface|d:reference|d:topic|
           d:sect1|d:section[not(parent::d:section)]">
    <xsl:call-template name="generate-content"/>
  </xsl:template>

</xsl:stylesheet>

The chunker.xsl file is more or less a stripped down version of the html/chunker.xsl file from the DocBook XSL stylesheets. It is used to create processor independent writing of our XML chunks. The file was integrated into dbsplit.xsl to make it more independent from the DocBook XSL stylesheets.

Example 3.7. chunker.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:saxon="http://icl.com/saxon"
  xmlns:lxslt="http://xml.apache.org/xslt"
  xmlns:redirect="http://xml.apache.org/xalan/redirect"
  xmlns:exsl="http://exslt.org/common"
  xmlns="http://www.w3.org/1999/xhtml" version="1.0"
  exclude-result-prefixes="saxon lxslt redirect exsl"
  extension-element-prefixes="saxon redirect lxslt exsl">

  <xsl:param name="chunker.output.method" select="'xml'"/>
  <xsl:param name="chunker.output.encoding" select="'UTF-8'"/>
  <xsl:param name="chunker.output.indent" select="'no'"/>
  <xsl:param name="chunker.output.omit-xml-declaration" select="'no'"/>
  <xsl:param name="chunker.output.standalone" select="'no'"/>
  <xsl:param name="chunker.output.doctype-public" select="''"/>
  <xsl:param name="chunker.output.doctype-system" select="''"/>
  <xsl:param name="chunker.output.media-type" select="''"/>
  <xsl:param name="chunker.output.cdata-section-elements" select="''"/>
  <xsl:param name="chunker.output.quiet" select="0"/>

  <xsl:param name="saxon.character.representation"
    select="'entity;decimal'"/>

  <xsl:template name="make-relative-filename">
    <xsl:param name="base.dir" select="'./'"/>
    <xsl:param name="base.name" select="''"/>

    <xsl:choose>
      <!-- put Saxon first to work around a bug in libxslt -->
      <xsl:when test="element-available('saxon:output')">
        <!-- Saxon doesn't make the chunks relative -->
        <xsl:value-of select="concat($base.dir,$base.name)"/>
      </xsl:when>
      <xsl:when test="element-available('exsl:document')">
        <!-- EXSL document does make the chunks relative, I think -->
        <xsl:choose>
          <xsl:when test="count(parent::*) = 0">
            <xsl:value-of select="concat($base.dir,$base.name)"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="$base.name"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:when test="element-available('redirect:write')">
        <!-- Xalan doesn't make the chunks relative -->
        <xsl:value-of select="concat($base.dir,$base.name)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes">
          <xsl:text>Don't know how to chunk with </xsl:text>
          <xsl:value-of select="system-property('xsl:vendor')"/>
        </xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="write.chunk">
    <xsl:param name="filename" select="''"/>
    <xsl:param name="quiet" select="$chunker.output.quiet"/>
    <xsl:param name="suppress-context-node-name" select="0"/>
    <xsl:param name="message-prolog"/>
    <xsl:param name="message-epilog"/>

    <xsl:param name="method" select="$chunker.output.method"/>
    <xsl:param name="encoding" select="$chunker.output.encoding"/>
    <xsl:param name="indent" select="$chunker.output.indent"/>
    <xsl:param name="omit-xml-declaration"
      select="$chunker.output.omit-xml-declaration"/>
    <xsl:param name="standalone" select="$chunker.output.standalone"/>
    <xsl:param name="doctype-public"
      select="$chunker.output.doctype-public"/>
    <xsl:param name="doctype-system"
      select="$chunker.output.doctype-system"/>
    <xsl:param name="media-type" select="$chunker.output.media-type"/>
    <xsl:param name="cdata-section-elements"
      select="$chunker.output.cdata-section-elements"/>

    <xsl:param name="content"/>

    <xsl:if test="$quiet = 0">
      <xsl:message>
        <xsl:if test="not($message-prolog = '')">
          <xsl:value-of select="$message-prolog"/>
        </xsl:if>
        <xsl:text>Writing </xsl:text>
        <xsl:value-of select="$filename"/>
        <xsl:if test="name(.) != '' and $suppress-context-node-name = 0">
          <xsl:text> for </xsl:text>
          <xsl:value-of select="name(.)"/>
          <xsl:if test="@id or @xml:id">
            <xsl:text>(</xsl:text>
            <xsl:value-of select="(@id|@xml:id)[1]"/>
            <xsl:text>)</xsl:text>
          </xsl:if>
        </xsl:if>
        <xsl:if test="not($message-epilog = '')">
          <xsl:value-of select="$message-epilog"/>
        </xsl:if>
      </xsl:message>
    </xsl:if>

    <xsl:choose>
      <xsl:when test="element-available('exsl:document')">
        <xsl:choose>
          <!-- Handle the permutations ... -->
          <xsl:when test="$media-type != ''">
            <xsl:choose>
              <xsl:when
                test="$doctype-public != '' and $doctype-system != ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-public="{$doctype-public}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:when
                test="$doctype-public != '' and $doctype-system = ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-public="{$doctype-public}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:when
                test="$doctype-public = '' and $doctype-system != ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:otherwise>
                <!-- $doctype-public = '' and $doctype-system = ''"> -->
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}" standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <xsl:otherwise>
            <xsl:choose>
              <xsl:when
                test="$doctype-public != '' and $doctype-system != ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-public="{$doctype-public}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:when
                test="$doctype-public != '' and $doctype-system = ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-public="{$doctype-public}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:when
                test="$doctype-public = '' and $doctype-system != ''">
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:when>
              <xsl:otherwise>
                <!-- $doctype-public = '' and $doctype-system = ''"> -->
                <exsl:document href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </exsl:document>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>

      <xsl:when test="element-available('saxon:output')">
        <xsl:choose>
          <!-- Handle the permutations ... -->
          <xsl:when test="$media-type != ''">
            <xsl:choose>
              <xsl:when
                test="$doctype-public != '' and $doctype-system != ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-public="{$doctype-public}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:when
                test="$doctype-public != '' and $doctype-system = ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-public="{$doctype-public}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:when
                test="$doctype-public = '' and $doctype-system != ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:otherwise>
                <!-- $doctype-public = '' and $doctype-system = ''"> -->
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  media-type="{$media-type}" standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <xsl:otherwise>
            <xsl:choose>
              <xsl:when
                test="$doctype-public != '' and $doctype-system != ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-public="{$doctype-public}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:when
                test="$doctype-public != '' and $doctype-system = ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-public="{$doctype-public}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:when
                test="$doctype-public = '' and $doctype-system != ''">
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  doctype-system="{$doctype-system}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:when>
              <xsl:otherwise>
                <!-- $doctype-public = '' and $doctype-system = ''"> -->
                <saxon:output
                  saxon:character-representation="{$saxon.character.representation}"
                  href="{$filename}" method="{$method}"
                  encoding="{$encoding}" indent="{$indent}"
                  omit-xml-declaration="{$omit-xml-declaration}"
                  cdata-section-elements="{$cdata-section-elements}"
                  standalone="{$standalone}">
                  <xsl:copy-of select="$content"/>
                </saxon:output>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>

      <xsl:when test="element-available('redirect:write')">
        <!-- Xalan uses redirect -->
        <redirect:write file="{$filename}">
          <xsl:copy-of select="$content"/>
        </redirect:write>
      </xsl:when>

      <xsl:otherwise>
        <!-- it doesn't matter since we won't be making chunks... -->
        <xsl:message terminate="yes">
          <xsl:text>Can't make chunks with </xsl:text>
          <xsl:value-of select="system-property('xsl:vendor')"/>
          <xsl:text>'s processor.</xsl:text>
        </xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="write.xml.chunk">
    <xsl:param name="filename" select="''"/>
    <xsl:param name="quiet" select="$chunker.output.quiet"/>
    <xsl:param name="suppress-context-node-name" select="0"/>
    <xsl:param name="message-prolog"/>
    <xsl:param name="message-epilog"/>
    <xsl:param name="method" select="'xml'"/>
    <xsl:param name="encoding" select="$chunker.output.encoding"/>
    <xsl:param name="media-type" select="$chunker.output.media-type"/>
    <xsl:param name="content"/>
    <xsl:call-template name="write.chunk">
      <xsl:with-param name="filename" select="$filename"/>
      <xsl:with-param name="quiet" select="$quiet"/>
      <xsl:with-param name="suppress-context-node-name"
        select="$suppress-context-node-name"/>
      <xsl:with-param name="message-prolog" select="$message-prolog"/>
      <xsl:with-param name="message-epilog" select="$message-epilog"/>
      <xsl:with-param name="method" select="$method"/>
      <xsl:with-param name="encoding" select="$encoding"/>
      <xsl:with-param name="indent" select="'yes'"/>
      <xsl:with-param name="omit-xml-declaration" select="'no'"/>
      <xsl:with-param name="standalone" select="'no'"/>
      <xsl:with-param name="doctype-public"/>
      <xsl:with-param name="doctype-system"/>
      <xsl:with-param name="media-type" select="$media-type"/>
      <xsl:with-param name="cdata-section-elements"/>
      <xsl:with-param name="content" select="$content"/>
    </xsl:call-template>
  </xsl:template>
</xsl:stylesheet>

It can be customized with several parameters:

base.dir

Determines the output directory

dbsplit.chunk.depth

Controls the depth where to split

dbsplit.ext

Defines the file extension for each filename written.

dbsplit.root.filename

Identifies the name of the root filename when splitted

use.id.as.filename

Uses ID values as filenames

rootid

Specify the root element to split

Splitting with the dbautosplit

The dbautosplit command is a Perl script which is only be available in openSUSE Linux distribution. As such, you can search for the command dbautosplit or download it from http://rpmfind.net/linux/rpm2html/search.php?query=dbautosplit. Additionaly it needs the XML-DOM and XML-RegExp packages. Download and install these package from CPAN, the Comprehensive Perl Archive Network, before you proceed.

For example, if you have a book with a preface and five chapters, use the following command to split it:

dbautosplit --level 1 MyBook.xml

It will generate this output:

Creating file : out/preface-01/index.xml
Creating file : out/chapter-01/index.xml
Creating file : out/chapter-02/index.xml
Creating file : out/chapter-03/index.xml
Creating file : out/chapter-04/index.xml
Creating file : out/chapter-05/index.xml
Creating file : out/index.xml

By default, dbautosplit creates an out/ directory. Each preface and chapter is stored in a separate subdirectory, named preface or chapter and with a consecutive number. The book itself is saved with xi:include elements in out/index.xml.

You can change the output behavior by using a template system similar to printf function (see manpage). If you want to name the chapters by their xml:id attribute, use the following commandline:

dbautosplit -l 1 -o "%attr(xml:id)%txt(.xml)" MyBook.xml

A possible output could look like this:

Creating file : out/preface.xml
Creating file : out/markup.xml
Creating file : out/common.xml
Creating file : out/structure.xml
Creating file : out/fo.xml
Creating file : out/html.xml
Creating file : out/index.xml

Discussion

The shown methods have both advantages and drawbacks.

Table 3.1. Comparison of Splitting Method
 splitdb/chunker.xsldbautosplittopic-maker-chunk.xsl
Change Splitting Level?yesyesyes
Special Support for Assemblies?nonoyes
    

See Also


Project@GitHubIssue#8