Re: [xsl] targeting the last element in a node or consecutive elements

Subject: Re: [xsl] targeting the last element in a node or consecutive elements
From: Chuck Bearden <cbearden@xxxxxxxx>
Date: Thu, 4 Jan 2007 16:00:04 -0600
Your second 'apply-templates' rule will omit text and comment nodes, 
but that may not be important to your use case.

I adapted Jarno Elovirta's tree walking design pattern [1] for your
problem.  It's rather verbose and probably overkill for your 
problem, but you can see that this pattern of node-by-node walking
is quite powerful and can be adapted to many uses.

[1] http://www.dpawson.co.uk/xsl/sect2/N4486.html#d5280e1102

--------------------------------------------------------------------
<?xml version="1.0"?>

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

  <xsl:output indent="yes" method="xml"/>

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="somenode">
    <xsl:param name="nodeType">start</xsl:param>
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <!-- Step into 'walk' mode and process first child node -->
      <xsl:apply-templates select="node()[1]" mode="walk">
        <xsl:with-param name="nodeType" select="$nodeType"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

<!-- 'walk' mode templates -->

  <!-- 'walk' mode; PI with no elements in following-sibling axis -->
  <xsl:template match="processing-instruction()
      [not(following-sibling::*)]" mode="walk">
    <xsl:param name="nodeType"/>
    <xsl:if test="$nodeType='elem'">
    <!-- We are in the first PI after the last element; 
         do any special processing here -->
      <insertedElement/><xsl:text>
</xsl:text>
    </xsl:if>
    <!-- Apply default mode templates to this node -->
    <xsl:apply-templates select="."/>
    <!-- Walk on to next sibling node -->
    <xsl:apply-templates select="following-sibling::node()[1]" mode="walk">
      <xsl:with-param name="nodeType">pi</xsl:with-param>
    </xsl:apply-templates>
  </xsl:template>

  <!-- 'walk' mode; all other nodes -->
  <xsl:template match="node()" mode="walk">
    <xsl:param name="nodeType"/>
    <!-- Apply default mode templates to this node -->
    <xsl:apply-templates select="."/>
    <!-- Walk on to next sibling node -->
    <xsl:apply-templates select="following-sibling::node()[1]" mode="walk">
    <!-- Tell following templates which of element or pi was last seen -->
      <xsl:with-param name="nodeType">
        <xsl:choose>
          <xsl:when test="self::*">elem</xsl:when>
          <xsl:when test="self::processing-instruction()">pi</xsl:when>
          <xsl:otherwise><xsl:value-of select="$nodeType"/></xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
    </xsl:apply-templates>
  </xsl:template>

<!-- Default mode templates; standard identity transforms -->
  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@*|text()|comment()">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="processing-instruction()">
  <!-- Special treatment for PIs? -->
    <xsl:copy/>
  </xsl:template>

</xsl:stylesheet>
--------------------------------------------------------------------

Chuck
--
Chuck Bearden ---------------------------------- C o n n e x i o n s
Sr. Content Specialist                        Rice University MS 375
cbearden@xxxxxxxx                                        PO Box 1892
AIM: Schleifheim                              Houston, TX 77251-1892
713.348.3661 (voice) --- <http://cnx.org/> ------ (fax) 713.348.3665

> From: egarrett@xxxxxxxxxxxx <egarrett@xxxxxxxxxxxx>
> Date: Jan 4, 2007 2:42 PM
> Subject: Re: [xsl] targeting the last element in a node or consecutive 
> elements
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> 
> 
> This appears to be working:
> 
>               <xsl:apply-templates select="node()[not(self::processing-
> instruction()[not(following-sibling::*)])]"/>
> 
> Then I insert the extra element.  Then grab the PIs I suppressed above:
>               <xsl:apply-templates select="node()[self::processing-
> instruction()[not(following-sibling::*)]]"/>
> 
> Does it look right?
> 
> Thanks.
> --emily
> 
> ----- Original Message -----
> From: <egarrett@xxxxxxxxxxxx>
> Date: Thursday, January 4, 2007 2:21 pm
> Subject: Re: [xsl] targeting the last element in a node or consecutive
> elements
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> 
> >Here is my logic so far to target everything in the node, and if
> >it is
> >a processing-instruction it should be processed only if something
> >exists after it other than another processing-instruction.  What I
> >have
> >below does not work because I think it is looking for the second
> >processing-instruction inside the first one rather than following
> >it,
> >but it is starting to feel like it is possible to put what I need
> >in
> >one instruction.
> >
> ><xsl:apply-templates select="node()[not(self::processing-instruction()
> >[not(following::*[not(self::processing-instruction())])])]"/>
> >
> >Warning: on line 58 of file:/C:/Documents%20and%
> >20Settings/egarrett/Desktop/Transforms%20R%20Us/saxon/xsl/X3B.xsl:
> > The self axis will never select any element() nodes when
> >starting at
> >an
> > processing-instruction() node
> >
> >Any ideas?
> >----- Original Message -----
> >From: egarrett@xxxxxxxxxxxx
> >Date: Thursday, January 4, 2007 9:59 am
> >Subject: Re: [xsl] targeting the last element in a node or
> >consecutive
> >elements
> >To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> >
> >> Yes, your example is correct.  I don't know if or how many PIs
> >> will
> >> occur at the end of the node.
> >>
> >> P.S. I started a new post because they changed the
> >> requirements....
> >>
> >> ----- Original Message -----
> >> From: Abel Braaksma <abel.online@xxxxxxxxx>
> >> Date: Thursday, January 4, 2007 9:53 am
> >> Subject: Re: [xsl] targeting the last element in a node or
> >> consecutive
> >> elements
> >> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> >>
> >> > Hi Emily,
> >> >
> >> > This is a double post, you already opened this discussion
> >under
> >> > "suppressing only the last PI" (which differed in details,
> >however).> >
> >> > If I may, it appears that you want to do this:
> >> >
> >> > <somenode>
> >> >    <node1 />
> >> >    <?instr1?>
> >> >    <node2 />
> >> >    <?instr2?>
> >> >    <?instr3?>
> >> > </somenode>
> >> >
> >> > 1. Process node1, instr1, node2 etc
> >> > 2. Do something before instr2 and instr3 (being consecutively
> >> and
> >> > together at the end of a node)
> >> > 3. Process instr2 and instr3
> >> > (if instr2 and instr3 were not there, you want to skip
> >> processing
> >> > step 2
> >> > and 3?)
> >> >
> >> > Am i right? Please confirm (and/or give your sample input, to
> >> make
> >> > the
> >> > subject easier)
> >> >
> >> > Cheers,
> >> > -- Abel
> >> >
> >> > Emily.Garrett@xxxxxxxxxxx wrote:
> >> > > I'm trying to use xsl:apply templates to grab the entire
> >> content
> >> > of a
> >> > > node, except for any PIs that may be at the very end of the
> >> > node.  I can
> >> > > see how position()=last() can be used to refer to the very
> >> last
> >> > PI, but
> >> > > how would I refer to possibly 2 or 3 PIs that are at the end
> >> of
> >> > the node
> >> > > (not knowing what they are or how many), but still process
> >PIs
> >> > that are
> >> > > contained elsewhere in this content?   Is there some syntax
> >> that
> >> > refers> to consecutive elements or refers to an element that
> >is
> >> > the last one in
> >> > > a node, not knowing what element it is?
> >> > >
> >> > > Here is explanation for what I'm trying to do:
> >> > >
> >> > > Process all content, unless they are PIs that occur
> >> > consecutively at the
> >> > > very end of the node.
> >> > >
> >> > > Then I have to insert an element, then process these PIs
> >that
> >> occur> > consecutively at the very end of the node.
> >> > >
> >> > > I'm having trouble narrowing down my research on this one.
> >> Does
> >> > anyone> have any ideas?
> >> > >
> >> > > Thanks,
> >> > > Emily

Current Thread