Re: [xsl] paras with nested block-level elements to XHTML mix of <p>s and flattened block-level elements

Subject: Re: [xsl] paras with nested block-level elements to XHTML mix of <p>s and flattened block-level elements
From: Syd Bauman <Syd_Bauman@xxxxxxxxx>
Date: Fri, 1 Aug 2008 15:28:51 -0400
> My question concerns X/HTML as the transformation target, where "p"
> is the lowest level block-level element, which admits no other
> block-level element(s) as content, but the source is a model (like
> DocBook, TEI, etc.) which allows (and has) a variety of block level
> content in paragraphs.

The following snippets of XSLT 2.0 are what I've been using to
transform <tei:p> elements that contain <tei:list> or <tei:eg> into
XHTML. I do not claim (at all) that this is a good, let alone the
best way to do this, but rather only that it has been working for me.

---------
  <!--
    ** This section contains all the code for moving <list>s and <eg>s that
    ** are nested inside <p>s to being the middle siblings of two <p>s. I.e.:
    ** | <p>Blah-blah-01
    ** |   <eg>exampleA</eg>
    ** |   Blah-blah-02
    ** |   <eg>exampleB</eg>
    ** |   Blah-blah-03</p>
    ** should be transformed into
    ** | <p>Blah-blah-01</p>
    ** | <pre>exampleA</pre>
    ** | <p>Blah-blah-02</p>
    ** | <pre>exampleB</pre>
    ** | <p>Blah-blah-03</p>
    -->
  <xsl:template match="in:p">
    <!-- first handle content up to and including the last <eg> or <list> inside me -->
    <xsl:call-template name="eg-and-list-in-p"/>
    <!-- Note that if there is no <eg> or <list>, the above template did nothing. -->
    <!-- So now we still need to handle the content from the last <eg> or <list> -->
    <!-- (exclusive of it) to the end-of-me, and all my content if there was no -->
    <!-- <eg> or <list>. So the first step is to select all such nodes and tuck 'em -->
    <!-- in a variable. So this XPath selects all nodes that are not <eg> or <list> -->
    <!-- and do not have any <eg> or <list> following. Thus, if there is neither -->
    <!-- an <eg> nor a <list>, it selects all my child nodes. -->
    <xsl:variable name="end-content"
      select="node()[not(self::in:list or self::in:eg) and not(following-sibling::in:list or following-sibling::in:eg)]"/>
    <xsl:if test="$end-content">
      <xsl:element name="p">
        <xsl:apply-templates select="$end-content"/>
      </xsl:element>
    </xsl:if>
  </xsl:template>
  
  <xsl:template name="eg-and-list-in-p">
    <!-- for each of my (this <p>'s) children <eg> or <list> ... -->
    <xsl:for-each select="./in:eg|./in:list">
      <!-- remember the <eg> or <list> before me (now "me" = current <eg> or <list>) -->
      <xsl:variable name="before-me" select="preceding-sibling::node()[self::in:eg or self::in:list][1]"/>
      <!-- 
        ** The content we want is everthing between it (previous <eg> or
        ** <list>, whichever it was) and me (the current <eg> or <list>).
        ** However, don't ask me exactly how this predicate works. While
        ** I understand each little piece, I think, I don't get the big
        ** picture, and I don't understand the detail of why the equality
        ** test could ever fail. It is modified from the incomparable Jeni
        ** Tennison's post to XSL-List of 2001-01-19T15:49Z "How to
        ** transform <BR> to </P><P>"
        ** (http://xslt.com/html/xsl-list/2001-01/msg00927.xhtml),
        ** which I found via Dave Pawson's awesomely helpful FAQ at
        ** http://www.dpawson.co.uk/xsl/index.xhtml
      -->
      <xsl:variable name="content"
        select="preceding-sibling::node()
        [not($before-me) or
        generate-id(preceding-sibling::node()[self::in:eg or self::in:list][1]) =
        generate-id($before-me)]" />
      <!-- if there is any content ... -->
      <xsl:if test="$content">
        <!-- ... put it out in a <p> before we ... -->
        <xsl:element name="p">
          <xsl:apply-templates select="$content"/>
        </xsl:element>
      </xsl:if>
      <!-- ... put current node (and its children) into the output tree. -->
      <xsl:apply-templates select="."/>
    </xsl:for-each>
  </xsl:template>
---------

Current Thread