Subject: Re: Move elements to preceding parent
From: Israel Viente <israel.viente@xxxxxxxxx>
Date: Wed, 17 Jun 2009 23:11:50 +0300
|
Hi Ken,
I really appreciate your code and comments, but after reading it many
times, I can't reach to the bottom of the logic here.
I'm a newbie so forgive my stupid questions.
1. Why do we need the outer most copy element:
> <xsl:template match="body">
> <xsl:copy>
How does it work in combination with xsl:for-each-group?
2. Can you please explain the group-ending-with selection?
Why do we need *[not(self::p)] ? Doesn't it mean all except p elements?
Thanks, Viente
On Sun, Jun 14, 2009 at 6:39 PM, G. Ken
Holman<gkholman@xxxxxxxxxxxxxxxxxxxx> wrote:
> At 2009-06-14 17:53 +0300, Israel Viente wrote:
>>
>> I am working with Saxon-B9.1 on the command line (XSLT 1.0 or 2.0 are OK).
>>
>> My input is something like the following:
>> ...
>> The reault output should be:
>
> Thank you for supplying complete examples to make the job of helping
easier.
>
>> For every span element that the class<>'chapter' verify that in every
>> p the last span element text ends with one character of .?"!
>> (paragraph ending char).
>> If it does, copy as is to the output.
>> Otherwise: Move the span elements from the next p to the current one
>> and remove the next p completely.
>>
>> I tried doing it with following-sibling & for-each , but I'm not sure
>> it is the right approach.
>
> That wasn't the approach that came to mind immediately for me. I've
learned
> over the years that the reach of following-sibling is usually too extensive
> to help out in many algorithms.
>
> With the advent of XSLT 2.0 what comes to mind immediately for me for such
> cases is grouping. And I think it fits well here, though I am a bit
> concerned your requirement may be under-specified. Certainly I could
> rewrite your requirement by considering where paragraphs need to be
> considered part of the same group when the span movement you need is being
> triggered.
>
> The code below groups all of the elements of interest such that the group
> always ends in a paragraph with the desired punctuation. Then I simply
copy
> the first as is and the spans and paragraph white-space of the others. I
> didn't know what to do with the NBSP characters of the others and you only
> said spans, so I only copied the white-space. Given this is paragraph
> content between the spans, perhaps all text nodes should be preserved and
> not just indentation. If so, just take the predicate off of the text()
> address.
>
> I hope this shows how to look at your problem as a grouping problem.
>
> . . . . . . . . . . Ken
>
>
> T:\ftemp>type viente.xml
> <?xml version="1.0" encoding="UTF-8"?>
> <!DOCTYPE html [<!ENTITY nbsp " ">]>
> <html xmlns="http://www.w3.org/1999/xhtml">
> <body>
> <p dir="rtl">
> <span class="chapter">line1</span>
> </p>
> <p dir="rtl"> <br />
> <span class="regular">line3.</span>
> <span class="italic">line4</span>
> <span class="regular">line5."</span>
> </p>
> <p dir="rtl"> <br />
> <span class="regular">line6.</span>
> <br />
> <span class="regular">line7</span>
> </p>
> <p dir="rtl"> <br />
> <span class="regular">line8.</span>
> <span class="regular">line9.</span>
> </p>
> </body>
> </html>
>
> T:\ftemp>call xslt2 viente.xml viente.xsl
> <?xml version="1.0" encoding="UTF-8"?><html
> xmlns="http://www.w3.org/1999/xhtml">
> <body><p dir="rtl">
> <span class="chapter">line1</span>
> </p><p dir="rtl">B B <br />
> <span class="regular">line3.</span>
> <span class="italic">line4</span>
> <span class="regular">line5."</span>
> </p><p dir="rtl">B B <br />
> <span class="regular">line6.</span>
> <br />
> <span class="regular">line7</span>
>
> <span class="regular">line8.</span>
> <span class="regular">line9.</span>
> </p></body>
> </html>
> T:\ftemp>type viente.xsl
> <?xml version="1.0" encoding="US-ASCII"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> xpath-default-namespace="http://www.w3.org/1999/xhtml"
> version="2.0">
>
> <xsl:output indent="no"/>
>
> <xsl:template match="body">
> <xsl:copy>
> <xsl:copy-of select="@*"/>
> <xsl:for-each-group select="*"
> group-ending-with="*[not(self::p)] |
> p[span/@class='chapter'] |
> p[matches(span[last()],
> '[.?"]$')]">
> <!--now the information is grouped by p elements that end as
> required-->
> <xsl:choose>
> <xsl:when test="current-group()[last()]
> [self::p][matches(span[last()],'[.?"]$')]">
> <!--in a group of p elements that end as required-->
> <xsl:copy>
> <xsl:copy-of select="@*"/>
> <!--preserve the content of the first of these p elements-->
> <xsl:apply-templates/>
> <!--preserve only the span elements and indentation from the
> rest;
> (the indentation is needed because this is paragraph
> white-space)-->
> <xsl:apply-templates select="current-group()[position()>1]/
> (text()[not(normalize-space())] |
> span)"/>
> </xsl:copy>
> </xsl:when>
> <xsl:otherwise>
> <!--in another kind of group so just copy these using identity-->
> <xsl:apply-templates select="current-group()"/>
> </xsl:otherwise>
> </xsl:choose>
> </xsl:for-each-group>
> </xsl:copy>
> </xsl:template>
>
> <xsl:template match="@*|node()"><!--identity for all other nodes-->
> <xsl:copy>
> <xsl:apply-templates select="@*|node()"/>
> </xsl:copy>
> </xsl:template>
>
> </xsl:stylesheet>
>
> T:\ftemp>rem Done!
>
>
>
> --
> XSLT/XSL-FO/XQuery hands-on training - Los Angeles, USA 2009-06-08
> Crane Softwrights Ltd. http://www.CraneSoftwrights.com/s/
> Training tools: Comprehensive interactive XSLT/XPath 1.0/2.0 video
> Video lesson: http://www.youtube.com/watch?v=PrNjJCh7Ppg&fmt=18
> Video overview: http://www.youtube.com/watch?v=VTiodiij6gE&fmt=18
> G. Ken Holman mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
> Male Cancer Awareness Nov'07 http://www.CraneSoftwrights.com/s/bc
> Legal business disclaimers: http://www.CraneSoftwrights.com/legal
|