Re: [xsl] Move elements to preceding parent

Subject: Re: [xsl] Move elements to preceding parent
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Sun, 14 Jun 2009 08:39:22 -0700
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 "&#xa0;">]> <html xmlns="http://www.w3.org/1999/xhtml";> <body> <p dir="rtl"> <span class="chapter">line1</span> </p> <p dir="rtl">&nbsp;&nbsp;<br /> <span class="regular">line3.</span> <span class="italic">line4</span> <span class="regular">line5."</span> </p> <p dir="rtl">&nbsp;&nbsp;<br /> <span class="regular">line6.</span> <br /> <span class="regular">line7</span> </p> <p dir="rtl">&nbsp;&nbsp;<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()],
                                                     '[.?&#x22;]$')]">
      <!--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()],'[.?&#x22;]$')]">
          <!--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

Current Thread