Difficulty: ★★★ (hard)
Keywords: bridgehead, section

Problem

You have a DocBook document which contains several bridgehead elements. The bridgehead has to be transformed into the correct section structure.

Solution

A bridgehead element is a “free-floating heading”. In most cases it is a bad idea as it is difficult to handle in XSLT. To create the correct section hierarchy, a stylesheet need to collect all nodes between a bridgehead element and the next one. The following stylesheet uses a set difference method:

Example 3.13. Transforms every bridgehead Element into a section Element
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"  
  xmlns:d="http://docbook.org/ns/docbook"
  xmlns="http://docbook.org/ns/docbook"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <xsl:import href="copy.xsl"/>
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:preserve-space elements="d:screen d:programlisting d:literallayout"/>
  
  <xsl:template match="d:section[d:bridgehead]">
    <!-- All nodes inside our section: -->
    <xsl:variable name="node1" select="node()"/>
    <!-- All nodes -->
    <xsl:variable name="node2" 
      select="d:bridgehead[1]|
              d:bridgehead[1]/following-sibling::node()"/>
    
    <!-- Copy our section with all attributes, apply the set difference
      and investigate the first bridgehead 
    -->
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select="$node1[count(.|$node2) != count($node2)]"/>
      <xsl:apply-templates select="d:bridgehead[1]"/>
    </xsl:copy>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
  <xsl:template match="d:bridgehead">
    <!-- All nodes who follow bridgeheads: -->
    <xsl:variable name="node1" 
      select="following-sibling::node()"/>
    <!-- All nodes who follow the next bridgehead including the next
         bridgehead.
         The next bridgehead element is included as we don't want it
         in the set difference:
    -->
    <xsl:variable name="node2"
      select="following-sibling::d:bridgehead[1]|
              following-sibling::d:bridgehead[1]/following-sibling::node()"/>   

    <!-- Create the section element with all attributes and apply
      standard rules for the diff set -->
    <xsl:element name="section">
      <xsl:copy-of select="@*"/>
      <xsl:text>&#10;  </xsl:text>
      <xsl:element name="title">
        <xsl:apply-templates select="node()"/>
      </xsl:element>
      <xsl:apply-templates select="$node1[count(.|$node2) != count($node2)]"/>
    </xsl:element>
    
    <!-- Process the next bridgehead -->
    <xsl:apply-templates select="following-sibling::d:bridgehead[1]"/>
  </xsl:template>
  
</xsl:stylesheet>

Discussion

The solution is unfortunately not very trivial in XSLT 1.0. The template rule d:section[d:bridgehead] matches only sections which contain one or more bridgehead elements. The template rule performs the following steps:

  1. Collect all nodes inside a section and save it in variable node1.

  2. Collect all nodes who follows the next bridgehead including the next bridgehead itself and save it in variable node2.

  3. Creates a section element and copy all attributes from the bridgehead element.

  4. Creates a title element and apply the content from the bridgehead element. This copies the content from bridgehead.

  5. Calculates the set difference between node1 and node2. This weird expression is needed in XSLT 1.0 to create a node set which contains only those nodes up to the first bridgehead.

  6. Handle the first bridgehead element which is covered by our bridgehead template rule.

The bridgehead template rule is responsible for transforming the current bridgehead element into a section. It is also responsible for the next bridgeheads. The rule performs the following steps:

  1. Collect all nodes following of the current bridgehead element and save it in variable node1.

  2. Collect all nodes following of the next bridgehead element including the next bridgehead element itself. Save the node set in variable node2.

  3. Creates a section element and copy all attributes from the bridgehead element.

  4. Creates a title element and apply the content from the bridgehead element. This copies the content from bridgehead.

  5. Calculate the set difference between node1 and node2 and apply the correct template rule (usually they are just copied).

  6. Close the section element and handle the next bridgehead element.


Project@GitHubIssue#8