[xsl] numbering nodes (by context | input), but not single ones

Subject: [xsl] numbering nodes (by context | input), but not single ones
From: "Fries, Markus, fiscus GmbH, Bonn" <M.Fries@xxxxxxxxxxx>
Date: Fri, 12 Apr 2002 19:27:34 +0200
Hi,

The files are only that long, because there are lots of comments. :)

I want to number the subitems. If they occur alone, no number should be
displayed,
otherwise the numbers should be 1, 2, ... like this

<item>
	<subitem>no number here</subitem>
</item>
<item>
	<subitem>          numbering should occur</subitem>
	<subitem>          numbering should occur</subitem>
	<subitem>          numbering should occur</subitem>
	<subitem>          numbering should occur</subitem>
</item>

Ok. Thats fine, with the templates I wrote. 
1. But I can not exclude those subitems with attributes. 

	<subitem typ="foo">not here</subitem>

See bottom of testNumber.xsl :


2. Does this cost a lot, and if yes is there a cheaper method?

    <xsl:if test="preceding-sibling::* or following-sibling::*">
      <xsl:value-of select="concat(' ', count(preceding-sibling::*) + 1)"/>
    </xsl:if>

I guess xslt 2.0 would save me from all that hazzle... Unfortunately I
am restricted to xalan.

Regards

Markus Fries



-- number.xsl

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

  <xsl:output method="xml"/>

  <!-- This stylesheet provides two modes for numbering -->

  <!-- mode="numberByContext"  Numbers the current nodeset according to
context.  
       As numbering is according to context, sorting is possible.
       Single nodes are not numbered. -->
  <!-- Be aware to put only the right nodes into context. 
       If comments, textnodes (especially whitespace) are included, 
       they will be counted and gaps in the numbers output appear. 
       Included elements with no output, will result in output of
       their number.  A "robust" behaviour would cost perfomance.  
       Usage: When you need to select a part of the source tree. 
              When it is not necessary to do anything else than
              performing the templates on the respective nodes and
              appending the number.
       
  -->

  <!-- "last() = 1"  i.e. the only element in the context.
       no number will be appended. -->
  <xsl:template match="*[last() = 1]" mode="numberByContext">
    <xsl:apply-templates select="."/>
  </xsl:template>

  <!--  "last() != 1" i.e the are more elements in the context. 
       the respective number will be appended.-->
  <xsl:template match="*[last() != 1]" mode="numberByContext">
    <xsl:apply-templates select="."/><xsl:value-of select="concat(' ',
position())"/>
  </xsl:template>



  <!-- mode="numberedByInput" Numbers current node according to the position
       in the source tree.  Single nodes are not numbered. -->
  <!-- Comments and textnodes that are siblings are ignored.  
       All elements on the sibling axis are counted, so an empty tag will
       result in numbers increased by 1 for following elements.
             <item>
               <noRefundPossible/>
               <subitem>eins</subitem>         
             </item>
       <subitem>eins</subitem> is the second child and thus numbered with 2.

       Usage: Applicable when the numbers can be drawn from the source tree
only.
              This restriction forbids sorting, and filters on the siblings.

              But it does allow to call the template with single nodes.
              Thus for-each loops can be used which add other stuff around
the
              result.
  -->
  <xsl:template match="*" mode="numberByInput">
    <xsl:apply-templates select="."/>
    <xsl:if test="preceding-sibling::* or following-sibling::*">
      <xsl:value-of select="concat(' ', count(preceding-sibling::*) + 1)"/>
    </xsl:if>
  </xsl:template>





  <!-- The template which should just return the number -->
  <xsl:template match="*" mode="getPositionInput">
    <xsl:if test="preceding-sibling::* or following-sibling::*">
      <xsl:value-of select="concat(' ', count(preceding-sibling::*) + 1)"/>
    </xsl:if>
  </xsl:template>


  <!-- The template which should just return the number -->
  <xsl:template match="*" mode="getPositionContext">
    <xsl:if test="last()!=1"><xsl:value-of select="concat(' ',
position())"/></xsl:if>
  </xsl:template>

</xsl:stylesheet>

----------------------------------------------------------------------------
------------

-- testNumber.xsl

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

  <xsl:output method="xml"/>

  <xsl:include href="number.xsl"/>
  

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

  <xsl:template match="text()">
  </xsl:template>

  <xsl:template match="text()" mode="numbered">
  </xsl:template>

  <xsl:template match="subitem">
    <xsl:value-of select="."/>
  </xsl:template>


  <xsl:template match="item">
    <!-- All that is commented out:
            <xsl:text>numbered, sorted: correct selection&#xa;</xsl:text>
            <xsl:apply-templates select="subitem" mode="numbered">
              <xsl:sort/>
            </xsl:apply-templates>
            <xsl:text>&#xa;</xsl:text>
            <xsl:text>numbered, unsorted: no selection, thus wrong
numbers&#xa;</xsl:text>
              <xsl:apply-templates mode="numbered"/>     
            <xsl:text>&#xa;</xsl:text>
            
            <xsl:text>incorrect usage of numbered: only one node in context
when used with for-each&#xa;</xsl:text>
            <xsl:for-each select="subitem">
              <xsl:apply-templates select="." mode="numbered"/>
<xsl:text>&#xa;</xsl:text>
            </xsl:for-each>
            
            <xsl:text>usage of numberedComplex: correct, if input contains
no other elements.&#xa;</xsl:text>
            
            <xsl:for-each select="subitem">
              <xsl:apply-templates select="." mode="numberedComplex"/>
<xsl:text>&#xa;</xsl:text>
            </xsl:for-each>
            <xsl:text>&#xa;</xsl:text>
    -->

    <!--
============================================================================
========== -->
    <!--     Why does  @typ!='foo' not work? They should not increment the
counter -->
    <!--     getPosition... must of course only return the number for the
last node. That should
             be easy. -->
    <!--
============================================================================
========== --> 
    
    <xsl:for-each select="subitem">
      <xsl:value-of select="."/>
      <xsl:apply-templates select="preceding-sibling::*[@typ!='foo']| ."
mode="getPositionContext"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>


</xsl:stylesheet>

----------------------------------------------------------------------------
-------------------------------

-- testNumber.xml

<testNumber>
      <item>
        <subitem>aOne</subitem>
	text
        <subitem typ="foo">dTwo</subitem>
	<noItem/>
        <subitem>cThree</subitem>
	<!-- comment -->
        <subitem>bFour</subitem>
      </item>
      <item> text
        <subitem>noNumber as single node</subitem>
      </item>    
</testNumber>

Markus Fries
fiscus GmbH
+49  228  28 07-273
markus.fries@xxxxxxxxxxx


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


Current Thread