Re: [xsl] empty elements to filled without overlapping hierachies

Subject: Re: [xsl] empty elements to filled without overlapping hierachies
From: James Cummings <James.Cummings@xxxxxxxxxxxxxx>
Date: Thu, 13 Nov 2003 11:23:49 +0000 (GMT)
On Wed, 12 Nov 2003, Jeni Tennison wrote:

> Let's try to find an XSLT 2.0 solution. It will use
> <xsl:for-each-group> with the group-starting-with option, which groups
> together items such that each group starts with the same thing.

This works, though I'm confused about one tiny bit, I'll mention further down, but
let me make sure I understand how it is working first.  (I do
*try* and learn rather than just leech your generous help...I'm just
not always that good at it ;-) )

> The first level of grouping is <SN>s. We want to group all the
> child nodes of the <body> element, aside from the <TITLE> element,
> into groups that start with a particular <SN> element. For each of
> these groups, we want to create a <SN> element with the value
> attribute from the original <SN>:
>
> <xsl:template match="body">
>   <body>
>     <xsl:copy-of select="TITLE" />
>     <xsl:for-each-group select="node() except TITLE"
>                         group-starting-with="SN">
>       <SN value="{@value}">
>         ...
>       </SN>
>     </xsl:for-each-group>
>   </body>
> </xsl:template>

So this takes any node() which includes the child elements and
any text() nodes and groups them together (except for TITLE)
as starting with SN.  I've never used @group-starting-with
before that does seem very handy.

> Next, we want to take the elements in that group (aside from the <SN>
> element itself) and group them into groups starting with <Q> elements.
> Now, there are three possible arrangements of these groups:
>
>   1. The very first group might not start with a <Q> element; in that
>      case, we just want to copy all the items in that group.
>
>   2. The group might end with a <SSD> element, in which case we want
>      to have a <Q> element that contains all the items in the group
>      aside from that final <SSD> element (and the <Q> element itself).
>
>   3. The group might end with something other than a <SSD> element, in
>      which case we want to have a <Q> element that contains all the
>      items in the group aside from the <Q> element itself
>
> <xsl:template match="body">
>   <body>
>     <xsl:copy-of select="TITLE" />
>     <xsl:for-each-group select="node() except TITLE"
>                         group-starting-with="SN">
>       <SN value="{@value}">
>         <xsl:for-each-group select="current-group() except ."
>                             group-starting-with="Q">

except .in this context being whatever is in group-starting-with above?

>           <xsl:choose>
>             <xsl:when test="not(self::Q)">
>               <xsl:copy-of select="." />
>             </xsl:when>

So case 1 above, copy anything that isn't Q

>             <xsl:when test="current-group()[last()][self::SSD]">
>               <Q value="{@value}">
>                 <xsl:copy-of select="(current-group() except .)
>                                        [position() != last()]" />
>               </Q>
>               <xsl:copy-of select="current-group()[last()]" />
>             </xsl:when>

So case 2 above, close the Q before the last SSD - this is where
my real question is.  If I don't include <xsl:strip-space
elements="body" /> This doesn't work right so you get:
 <Q value="foo">blah blah blah
 <SSD value="blah"/></Q>
instead of
 <Q value="foo">blah blah blah
 </Q><SSD value="blah"/>
but I'm not confident about what that is.  Is it because the
original might have had something like:
 <Q value="foo"/>blah blah blah
 <SSD value="blah"/>
 [whitespacenode]

Or am I misunderstanding that?

>             <xsl:otherwise>
>               <Q value="{@value}">
>                 <xsl:copy-of select="current-group() except ." />
>               </Q>
>             </xsl:otherwise>
>           </xsl:choose>
>         </xsl:for-each-group>
>       </SN>
>     </xsl:for-each-group>
>   </body>
> </xsl:template>

Is there simultaneously an easy way to add in additional markup for the
individual lines inside of Q? i.e. either: <l>blah</l> or blah<lb/>?
so:
 <Q value="foo"><l>blah blah blah</l>
 <l>blah blah blah blah blah</l>
 <l>blah blah blah blah blah</l><SSD value="foo"/>
 <l>blah blah blah blah blah</l></Q>
 <SSD value="blort"/>


> An XSLT 1.0 solution would be harder...

Yes, certainly.  I'm assuming it isn't necessarily impossible?  But
XSLT2 is fine with me ;-)

Offlist someone suggested the route of doing COCOA/OCP -> SGML with
omitted end-tags, create DTD specifying the relationships of
SN/Q/SSD, then sgml2xml conversion.  This leads to any SSD at
the very end of a Q being included inside the Q since SSD has
to be allowed inside Q at some points.

Many Thanks,

-James


---
Dr James Cummings, Oxford Text Archive, James.Cummings@xxxxxxxxxxxxxx

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


Current Thread