xsl-list
[Top] [All Lists]

FW: [XSL] extracting a verse

2002-12-22 17:25:45
I posted this message on Sunday but it hasn't appeared yet. Sorry if you see
this twice:

 -----Original Message-----
From:   Conal Tuohy [mailto:conalt(_at_)paradise(_dot_)net(_dot_)nz]
Sent:   Sunday, 22 December 2002 17:43
To:     xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject:        RE: [xsl] [XSL] extracting a verse

Here's a style-sheet I wrote to do this kind of thing. I'm a bit embarrassed
to post it because it's klunky and not really generalized (it was done in a
rush and never optimized) but it does the trick.

This was actually used in a pipelined process (inside Cocoon) to process
some TEI documents prior to feeding them into another suite of
TEI-processing stylesheets. I had  to preserve <pb/> (page breaks) in the
TEI which can fall in the middle of someone's name, inside an item inside a
list, etc... I didn't want to edit the suite of stylesheets because it would
have required reading and making small changes to dozens of complex existing
templates, so instead I used this stylesheet to "promote" the <pb/> elements
and split the <name>, <item>, <list> etc, etc, elements where necessary.

Because it's a stage in a pipeline, it simply copies each partition with
<xsl:copy> and <xsl:copy-of>, but it could easily be modified to work in a
non-pipelined environment by using <apply-templates
mode="process-partition"> to process each partitioned "range" of the
document (i.e. each bible verse, in the example under discussion).

Cheers!

Con

<!--
   make-partitions.xsl

   Implements a kind of "dismemberment" transformation to a document.
   It copies the document, while dismembering certain parts.

   To use, call the template "make-partitions" with context node being the
node to partition.
   Specify the list of delimiters using the "delimiters" nodeset parameter.
The nodes in this
   set should all be descendants of the context node.
   If the context node has n delimiters it will be copied n+1 times, each
copy containing
   the tree of descendants between each delimiter. In between the n+1 copies
will be the
   delimiters themselves, which will have become siblings of the node that
was originally
   their ancestor.

   e.g. to move <hr> elements out from inside <p> elements up to the same
level:

   <xsl:template match="p">
      <xsl:call-template name="make-partitions">
         <xsl:with-param name="delimiters" select=".//hr"/>
      </xsl:call-template>
   </xsl:template>

   This will transform the following fragment:

    <p>
        Blah blah blah
        <hr/>
        blah blah
        <i>
            blah blah
            <hr/>
            blah blah
        </i>
        blah.
    </p>

    <p>
        Blah blah blah
    </p>
    <hr/>
    <p>
        blah blah
        <i>
            blah blah
        </i>
    </p>
    <hr/>
    <p>
        <i>
            blah blah
        </i>
        blah.
    </p>

-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

   <xsl:template match="*" mode="get-partition">
      <xsl:param name="start-delimiter-id"/>
      <xsl:param name="end-delimiter-id"/>
      <xsl:variable name="parent" select="."/>

      <xsl:variable name="start-delimiter-pos">
         <xsl:call-template name="get-delimiter-position">
            <xsl:with-param name="delimiter-id"
select="$start-delimiter-id"/>
            <xsl:with-param name="context-list" select="$parent/node()"/>
         </xsl:call-template>
      </xsl:variable>

      <xsl:variable name="end-delimiter-pos">
         <xsl:call-template name="get-delimiter-position">
            <xsl:with-param name="delimiter-id" select="$end-delimiter-id"/>
            <xsl:with-param name="context-list" select="$parent/node()"/>
         </xsl:call-template>
      </xsl:variable>

      <xsl:choose>
         <xsl:when test="$end-delimiter-pos > 0 or $start-delimiter-pos >
0">
            <!-- at least one of the delimiters is present -->

            <xsl:copy>
               <xsl:copy-of select="@*"/>
               <xsl:choose><!-- how to partition the children -->
                  <xsl:when test="$start-delimiter-pos > 0 and
$end-delimiter-pos=$start-delimiter-pos">
                     <!-- delimiters are both children of the same child of
the current node -->
                     <!-- recurse, and allow this child node to partition
itself -->
                     <xsl:apply-templates mode="get-partition"
select="$parent/node()[position()=$start-delimiter-pos]">
                        <xsl:with-param name="start-delimiter-id"
select="$start-delimiter-id"/>
                        <xsl:with-param name="end-delimiter-id"
select="$end-delimiter-id"/>
                     </xsl:apply-templates>
                  </xsl:when>
                  <xsl:otherwise>
                     <!-- delimiters are not contained entirely within a
child of the current node -->
                     <!-- is start-delimiter inside a child? -->
                     <xsl:if test="$start-delimiter-pos > 0">
                        <xsl:if
test="not($parent/node()[generate-id()=$start-delimiter-id])">
                           <xsl:apply-templates mode="get-partition"
select="$parent/node()[position()=$start-delimiter-pos]">
                              <xsl:with-param name="start-delimiter-id"
select="$start-delimiter-id"/>
                              <xsl:with-param name="end-delimiter-id"
select="$end-delimiter-id"/>
                           </xsl:apply-templates>
                        </xsl:if>
                     </xsl:if>
                     <!-- handle any children between delimiters or between
children containing delimiters -->
                     <xsl:choose>
                        <xsl:when test="$end-delimiter-pos>0">
                           <xsl:copy-of select="$parent/node()[position() >
$start-delimiter-pos and $end-delimiter-pos > position()]"/>
                        </xsl:when>
                        <xsl:otherwise>
                           <xsl:copy-of select="$parent/node()[position() >
$start-delimiter-pos]"/>
                        </xsl:otherwise>
                     </xsl:choose>
                     <!-- is end-delimiter inside a child? -->
                     <xsl:if test="$end-delimiter-pos > 0">
                        <xsl:if
test="not($parent/node()[generate-id()=$end-delimiter-id])">
                           <xsl:apply-templates mode="get-partition"
select="$parent/node()[position()=$end-delimiter-pos]">
                              <xsl:with-param name="start-delimiter-id"
select="$start-delimiter-id"/>
                              <xsl:with-param name="end-delimiter-id"
select="$end-delimiter-id"/>
                           </xsl:apply-templates>
                        </xsl:if>
                     </xsl:if>
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:copy>
         </xsl:when>
         <xsl:otherwise>
            <!-- no delimiters present - use copy-of to copy this
sub-tree -->
            <xsl:copy-of select="."/>
         </xsl:otherwise>
      </xsl:choose>

   </xsl:template>



   <xsl:template name="get-delimiter-position">
      <xsl:param name="delimiter-id"/>
      <xsl:param name="context-list"/>
      <xsl:variable name="position">
         <xsl:for-each select="$context-list">
            <xsl:variable name="current-position" select="position()"/>
            <xsl:choose>
               <xsl:when test="generate-id(.)=$delimiter-id">
                  <xsl:value-of select="$current-position"/>
               </xsl:when>
               <xsl:when test=".//*[generate-id(.)=$delimiter-id]">
                  <xsl:value-of select="$current-position"/>
               </xsl:when>
            </xsl:choose>
         </xsl:for-each>
      </xsl:variable>
      <xsl:choose>
         <xsl:when test="$position>0">
            <xsl:value-of select="$position"/>
         </xsl:when>
         <xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
      </xsl:choose>
   </xsl:template>


   <xsl:template name="make-partitions">
      <!-- if the element contains delimiters, partition it, otherwise, copy
it -->
      <xsl:param name="delimiters"/>
      <xsl:variable name="current-node" select="."/>
      <xsl:choose>
         <xsl:when test="$delimiters">
            <!-- the current element includes children which should
partition it -->
            <xsl:variable name="context-list" select="./node()"/>
            <!-- get the first partition -->
            <xsl:variable name="first-partition-delimiter-id"
select="generate-id($delimiters[position()=1])"/>
            <xsl:apply-templates mode="get-partition" select=".">
               <xsl:with-param name="end-delimiter-id"
select="$first-partition-delimiter-id"/>
            </xsl:apply-templates>
            <!-- copy the delimiter itself -->
            <xsl:copy-of select="$delimiters[position()=1]"/>
            <!-- intermediate partitions -->
            <xsl:for-each select="$delimiters">
               <!-- do all except the final partition, which must have no
end-delimiter -->
               <xsl:if test="position()>1">
                  <xsl:variable name="start-delimiter-index"
select="position()-1"/>
                  <xsl:variable name="start-delimiter-id"
select="generate-id($delimiters[position()=$start-delimiter-index])"/>
                  <xsl:apply-templates mode="get-partition"
select="$current-node">
                     <xsl:with-param name="start-delimiter-id"
select="$start-delimiter-id"/>
                     <xsl:with-param name="end-delimiter-id"
select="generate-id(.)"/>
                  </xsl:apply-templates>
                  <!-- copy the delimiter itself -->
                  <xsl:copy-of select="."/>
               </xsl:if>
            </xsl:for-each>
            <!-- final partition -->
            <xsl:apply-templates mode="get-partition" select=".">
               <xsl:with-param name="start-delimiter-id"
select="generate-id($delimiters[position()=last()])"/>
            </xsl:apply-templates>
         </xsl:when>
         <xsl:otherwise>
            <!-- contains no delimiters - just copy the element normally -->
            <xsl:copy-of select="."/>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

   <!-- identity transformation otherwise -->
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>



</xsl:stylesheet>








 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>