IX - Patterns de programmation
        Inclusion conditionnelle de feuille de style
                Motivation
                Réalisation
        Fonction
                Motivation
                Réalisation
                Exemple
        Action
                Motivation
                Réalisation
                Exemple
        Itération
                Motivation
                Réalisation récursive
                        Exemple
                Itération par la méthode de Piez
                        Exemple
        Récursion
                Motivation
                Réalisation
                Exemple du décompte de mots dans une chaîne
        Visiteur récursif de node-set
                Motivation
                Réalisation
                Application
                        Minimum d'un node-set
                        Somme des valeurs d'un node-set
        Fonction renvoyant plusieurs résultats
                Motivation
                Réalisation
                Exemple
        Utilisation d'une structure de données auxilaire
                Motivation
                Exemple
        Identité de nœuds et node-set de valeurs toutes différentes
                Motivation
                Réalisation
                        Tests d'identité
                        Node-set de valeurs toutes différentes


Chapitre IX - Patterns de programmation

Inclusion conditionnelle de feuille de style

Motivation

Réalisation

Fonction

Motivation

Réalisation

Modèle nommé renvoyant une valeur
<xsl:template name="xxx"> 
    <xsl:param name="yyy"/> 
    <xsl:param name="zzz"/>
    
    <xsl:variable name="result">
        ... corps de la "fonction" ici ...
    </xsl:variable>
    
    <xsl:copy-of select="$result" />
</xsl:template>
Appel du modèle nommé renvoyant une valeur
<xsl:variable name="result-xxx">
    <xsl:call-template name="xxx">
        <xsl:with-param name="yyy" select="..."/>
        <xsl:with-param name="zzz" select="..."/>
    </xsl:call-template>
</xsl:variable>

Exemple

Fonction index-of
<!-- renvoie l'indice du début de 'testString' dans 'aString' -->
<!-- renvoie -1 si 'aString' ne contient pas 'testString'  -->

<xsl:template name="index-of"> 
    <xsl:param name="aString"/> 
    <xsl:param name="testString"/>
    
    <xsl:variable name="result">
        <xsl:choose>
            <xsl:when test=" contains( $aString, $testString ) ">
                <xsl:variable name="string-Before">
                    <xsl:value-of 
                    select="substring-before( $aString, $testString )" />
                </xsl:variable>
                <xsl:value-of select="1+string-length( $string-Before )" />
            </xsl:when>
            
            <xsl:otherwise>
                -1
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    
    <xsl:copy-of select="$result" />
</xsl:template>
Appel de la fonction index-of
<!-- indice de la première apostrophe dans 'stringToSplit' -->
<xsl:variable name="indexOf-firstApos">
    <xsl:call-template name="index-of">
        <xsl:with-param name="aString" select="$stringToSplit"/>
        <xsl:with-param name="testString" select='"&apos;"'/>
    </xsl:call-template>
</xsl:variable>

<!-- indice du premier espace dans 'stringToSplit' -->
<xsl:variable name="indexOf-firstSpace">
    <xsl:call-template name="index-of">
        <xsl:with-param name="aString" select="$stringToSplit"/>
        <xsl:with-param name="testString" select='" "'/>
    </xsl:call-template>
</xsl:variable>
...

Action

Motivation

Réalisation

Modèle nommé réalisant une action
<xsl:template name="instancier-xxx"> 
    <xsl:param name="yyy"/> 
    <xsl:param name="zzz"/>
    
    ... corps du modèle de transformation ici ...

</xsl:template>

Exemple

Action instancier-bourre
<!-- 'bourre' : le caractère de rembourrage avec l'espace comme valeur par défaut  -->

<xsl:template name="instancier-bourre">  
    <xsl:param name="bourre" select="' '"/>
    <xsl:value-of select="$bourre" />
</xsl:template>

Itération

Motivation

Réalisation récursive

Modèle nommé réalisant la répétition d'une action
<xsl:template name="iter-instancier-xxx">  
    <xsl:param name="n"/>  
    <xsl:param name="..."/>
    
    <xsl:if test="$n > 0">
    
        <xsl:call-template name="instancier-xxx">
            <xsl:with-param name="..." select="..."/>
        </xsl:call-template>
        
        <xsl:call-template name="iter-instancier-xxx">
            <xsl:with-param name="n" select="$n - 1"/>
            <xsl:with-param name="..." select="..."/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>
Modèle nommé réalisant la répétition d'une action
<xsl:template name="iter-instancier-xxx">  
    <xsl:param name="n"/>  
    <xsl:param name="..."/>
    
    <xsl:if test="$n > 0">
    
        <!-- instancier ici directement des instructions XSLT -->
        
        <xsl:call-template name="iter-instancier-xxx">
            <xsl:with-param name="n" select="$n - 1"/>
            <xsl:with-param name="..." select="..."/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

Exemple

itérations.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output  method='text' encoding='ISO-8859-1' />

    <xsl:template name="instancier-bourre">  
        <xsl:param name="bourre"/>
        <xsl:value-of select="$bourre" />
    </xsl:template>
    
    <xsl:template name="iter-instancier-bourre">  
        <xsl:param name="n"/>  
        <xsl:param name="bourre"/>
        
        <xsl:if test="$n > 0">
            <xsl:call-template name="instancier-bourre">
                <xsl:with-param name="bourre" select="$bourre"/>
            </xsl:call-template>
            <xsl:call-template name="iter-instancier-bourre">
                <xsl:with-param name="n" select="$n - 1"/>
                <xsl:with-param name="bourre" select="$bourre"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    
    <xsl:template match="/">
        <xsl:call-template name="iter-instancier-bourre">
            <xsl:with-param name="n">7</xsl:with-param>
            <xsl:with-param name="bourre">.</xsl:with-param>
        </xsl:call-template>
    </xsl:template>
    
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
.......
Récupération du flux de sortie dans une variable
    <xsl:template match="/">
        <xsl:variable name="rembourrage">
            <xsl:call-template name="iter-instancier-bourre">
                <xsl:with-param name="n">7</xsl:with-param>
                <xsl:with-param name="bourre">.</xsl:with-param>
            </xsl:call-template>
        </xsl:variable>
        ... utilisation de la variable $rembourrage ...
    </xsl:template>

Itération par la méthode de Piez

Itération Piez
<xsl:for-each select="$NS[ position() &lt; $n+1 ]" >  
   
    <xsl:call-template name="instancier-xxx">
        <xsl:with-param name="..." select="..."/>
    </xsl:call-template>
    
</xsl:for-each>
document('')//node()

Exemple

itérations.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output  method='text' encoding='ISO-8859-1' />

    <xsl:variable name="PuitsDeNoeuds" select="document('')//node()"/>
    
    <xsl:template name="instancier-bourre">  
        <xsl:param name="bourre"/>
        <xsl:value-of select="$bourre" />
    </xsl:template>
    
    <xsl:template name="iter-instancier-bourre">  
        <xsl:param name="n"/>  
        <xsl:param name="bourre"/>
        
        <xsl:for-each select="$PuitsDeNoeuds[ position() &lt; $n+1 ]" >
            <xsl:call-template name="instancier-bourre">
                <xsl:with-param name="bourre" select="$bourre"/>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    
    <xsl:template match="/">
        <xsl:call-template name="iter-instancier-bourre">
            <xsl:with-param name="n">7</xsl:with-param>
            <xsl:with-param name="bourre">.</xsl:with-param>
        </xsl:call-template>
    </xsl:template>
    
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
.......

Récursion

Motivation

Réalisation

Exemple du décompte de mots dans une chaîne

nombreEspaces( str ) =
 0 si la chaîne str ne contient auncun espace;
 1 + nombreEspaces( substring-after( str, ' ') ) sinon.
Fonction nombreEspaces( str )
<xsl:template name="nombreEspaces">  
    <xsl:param name="str"/>
    <xsl:variable name="result">
        <xsl:choose>
            <xsl:when test="contains( $str, ' ' ) ">
                <xsl:variable name="nombreEspaces-recursif">
                    <xsl:call-template name="nombreEspaces">
                        <xsl:with-param name="str" 
                        select="substring-after( $str, ' ')"/>
                    </xsl:call-template>
                </xsl:variable> 
                <xsl:value-of select="1 + $nombreEspaces-recursif" />
            </xsl:when>
            <xsl:otherwise>
                0
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:copy-of select="$result"/>
</xsl:template>
nombreEspaces( str, nombre-courant ) =
 nombre-courant si la chaîne str ne contient auncun espace;
 nombreEspaces( substring-after( str, ' '), nombre-courant + 1 ) sinon.
instancier-nombreEspaces( str, nombre-courant ), c'est :
- instancier nombre-courant si la chaîne str ne contient auncun espace;
    
- instancier-nombreEspaces( substring-after( str, ' '), nombre-courant + 1 )
  sinon.
Action instancier-nombreEspaces( str, nombre-courant )
<xsl:template name="instancier-nombreEspaces">  
    <xsl:param name="str"/> 
    <xsl:param name="nombre-courant" select="'0'"/>
    <xsl:choose>
        <xsl:when test="contains( $str, ' ' ) ">
            <xsl:call-template name="instancier-nombreEspaces">
                <xsl:with-param name="str" 
                select="substring-after( $str, ' ')"/>
                
                <xsl:with-param name="nombre-courant" 
                select="1 + $nombre-courant"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$nombre-courant"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
NombreMots.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.1"> <!-- compatibilité Saxon 6.5 -->

    <xsl:output  method='text' encoding='ISO-8859-1' />

    <xsl:template name="nombreEspaces">  
        <xsl:param name="str"/>
        <xsl:variable name="result">
            <xsl:choose>
                <xsl:when test="contains( $str, ' ' ) ">
                    <xsl:variable name="nombreEspaces-recursif">
                        <xsl:call-template name="nombreEspaces">
                            <xsl:with-param name="str" 
                            select="substring-after( $str, ' ')"/>
                        </xsl:call-template>
                    </xsl:variable> 
                    <xsl:value-of select="1 + $nombreEspaces-recursif" />
                </xsl:when>
                <xsl:otherwise>
                    0
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:copy-of select="$result"/>
    </xsl:template>
    
    <xsl:template name="instancier-nombreEspaces">  
        <xsl:param name="str"/> 
        <xsl:param name="nombre-courant" select="'0'"/>
        <xsl:choose>
            <xsl:when test="contains( $str, ' ' ) ">
                <xsl:call-template name="instancier-nombreEspaces">
                    <xsl:with-param name="str" 
                    select="substring-after( $str, ' ')"/>
                    
                    <xsl:with-param name="nombre-courant" 
                    select="1 + $nombre-courant"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$nombre-courant"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
    
    <xsl:variable name="complainteDuCharretier">
        Pousser des charettes à longueur de journée réclame de l'énergie
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:variable name="N1">
            <xsl:call-template name="nombreEspaces">
                <xsl:with-param name="str" 
                select="normalize-space($complainteDuCharretier)"/>
            </xsl:call-template>
        </xsl:variable>
        Nombre de mots = <xsl:value-of select="1 + $N1"/>
        
        <xsl:variable name="N2">
            <xsl:call-template name="instancier-nombreEspaces">
                <xsl:with-param name="str" 
                select="normalize-space($complainteDuCharretier)"/>
            </xsl:call-template>
        </xsl:variable>
        Nombre de mots = <xsl:value-of select="1 + $N2"/>
    </xsl:template>
    
    
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
        Nombre de mots = 10
        Nombre de mots = 10

Visiteur récursif de node-set

Motivation

Réalisation

instancier-resultat( ns, resultat-courant ), c'est :
- instancier resultat-courant si ns est vide;
    
- instancier-resultat( ns[position() > 1], 
                       resultat( 
                           resultat-courant, 
                           valeur(ns[position() = 1])
                       ) 
  ) sinon.

Application

Minimum d'un node-set

Maisons.xml
<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<maisons>
    <maison id="1">
        <RDC>
            <cuisine surface='12m2'>
                Evier inox. Mobilier encastré.
            </cuisine>
            <WC>
                Lavabo. Cumulus 200L.
            </WC>
            <séjour surface='40m2'>
                Cheminée en pierre. Poutres au plafond. 
                Carrelage terre cuite. Grande baie vitrée.
            </séjour>
            <bureau surface='15m2'>
                Bibliothèque encastrée.
            </bureau>
            <garage/>
        </RDC>
        <étage>
            <terrasse>Palmier en zinc figurant le désert.</terrasse>
            <chambre surface='28m2' fenêtre='3'>
                Carrelage terre cuite poncée.
                <alcôve surface='8m2' fenêtre='1'>
                    Lambris.
                </alcôve>
            </chambre>
            <chambre surface='18m2'>
                Lambris.
            </chambre>
            <salleDeBains surface='15m2'>
                Douche, baignoire, lavabo.
            </salleDeBains>
        </étage>
    </maison>
    <maison id="2">
        <RDC>
            <cuisine surface='12m2'>
                en ruine.
            </cuisine>
            <garage/>
        </RDC>
        <étage>
            <mirador surface="1m2">
                Vue sur la mer. Idéal en cas de tempête.
            </mirador>
            <salleDeBains surface='15m2'>
                Douche.
            </salleDeBains>
        </étage>
    </maison>
    <maison id="3">
        <RDC>
            <séjour surface='40m2'>
                Les plaisirs ont choisi pour asile
                Ce séjour agréable et tranquille.
                Que ces lieux sont charmants
                Pour les heureux amants.
            </séjour>
        </RDC>
        <étage>
            <chambre surface='17.5m2'>
                Exposition plein sud.
            </chambre>
        </étage>
    </maison>
</maisons>
fonction valeur()
<xsl:template name="valeur">  
    <xsl:param name="unSingleton"/>
    
    <xsl:variable name="result"> <!-- une surface -->
        <xsl:variable name="surface" select="$unSingleton/attribute::surface"/>
        
        <xsl:choose>
            <xsl:when test="$surface"> 
                <xsl:value-of select="substring-before( $surface, 'm' )" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="number('NaN')" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:copy-of select="$result" />
</xsl:template>
fonction minimum()
<xsl:template name="minimum">  
    <xsl:param name="v1"/>  
    <xsl:param name="v2"/>
    
    <xsl:variable name="result"> 
        <xsl:choose>
            <xsl:when test="string($v1) = 'NaN' and string($v2) = 'NaN'"> 
                <xsl:value-of select="$v1" />
            </xsl:when>
            <xsl:when test="string($v1) = 'NaN'"> 
                <xsl:value-of select="$v2" />
            </xsl:when>
            <xsl:when test="string($v2) = 'NaN'"> 
                <xsl:value-of select="$v1" />
            </xsl:when>
            <xsl:when test="$v1 > $v2"> 
                <xsl:value-of select="$v2" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$v1" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:copy-of select="$result" />
</xsl:template>
SurfaceMini.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output  method='text' encoding='ISO-8859-1' />

    <!-- ============================================================== -->
    <xsl:template name="valeur">  
        <xsl:param name="unSingleton"/>
        
        <xsl:variable name="result"> <!-- une surface -->
            <xsl:variable name="surface" 
            select="$unSingleton/attribute::surface"/>
            
            <xsl:choose>
                <xsl:when test="$surface"> 
                    <xsl:value-of select="substring-before( $surface, 'm' )"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="number('NaN')" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:copy-of select="$result" />
    </xsl:template>

    <!-- ============================================================== -->
    <xsl:template name="minimum">  
        <xsl:param name="v1"/>  
        <xsl:param name="v2"/>
        
        <xsl:variable name="result"> 
            <xsl:choose>
                <xsl:when test="string($v1) = 'NaN' and string($v2) = 'NaN'">
                    <xsl:value-of select="$v1" />
                </xsl:when>
                <xsl:when test="string($v1) = 'NaN'"> 
                    <xsl:value-of select="$v2" />
                </xsl:when>
                <xsl:when test="string($v2) = 'NaN'"> 
                    <xsl:value-of select="$v1" />
                </xsl:when>
                <xsl:when test="$v1 > $v2"> 
                    <xsl:value-of select="$v2" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$v1" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:copy-of select="$result" />
    </xsl:template>
        
    <!-- ============================================================== -->
    <xsl:template name="instancier-min">  
        <xsl:param name="unNodeSet" />  
        <xsl:param name="min-courant" />
        
        <xsl:choose>
            <xsl:when test="$unNodeSet">
                <xsl:call-template name="instancier-min">
                    <xsl:with-param name="unNodeSet" 
                                    select="$unNodeSet[position() > 1]"/>
                    
                    <xsl:with-param name="min-courant">
                        <xsl:call-template name="minimum">
                            <xsl:with-param name="v1" select="$min-courant"/>
                            
                            <xsl:with-param name="v2">
                                <xsl:call-template name="valeur">
                                    <xsl:with-param name="unSingleton"
                                    select="$unNodeSet[position() = 1]"/>
                                </xsl:call-template>
                            </xsl:with-param>
                        </xsl:call-template>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            
            <xsl:otherwise>
                <xsl:value-of select="$min-courant" />
            </xsl:otherwise>
        </xsl:choose>
        
    </xsl:template>
    
    <!-- ============================================================== -->
    <xsl:template match="/">
        <xsl:variable name="surface-mini">
            <xsl:call-template name="instancier-min">
                <xsl:with-param name="unNodeSet" select="//*"/>
                <xsl:with-param name="min-courant" select="1000000"/>  
            </xsl:call-template>
        </xsl:variable>
        Surface mini = <xsl:value-of select="$surface-mini" />m2
        nature de la pièce = <xsl:value-of select="local-name(
                              //*[attribute::surface = 
                                         concat($surface-mini, 'm2')])" />
    </xsl:template>
    
    <!-- ============================================================== -->
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
        Surface mini = 1m2
        nature de la pièce = mirador

Somme des valeurs d'un node-set

SurfaceHabitable.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output  method='text' encoding='ISO-8859-1' />

    <!-- ============================================================== -->
    <xsl:template name="valeur">  
        <xsl:param name="unSingleton"/>
        
        <xsl:variable name="result"> <!-- une surface -->
            <xsl:variable name="surface" 
                          select="$unSingleton/attribute::surface"/>
            
            <xsl:choose>
                <xsl:when test="$surface"> 
                    <xsl:value-of select="substring-before( $surface, 'm' )" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="number('NaN')" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:copy-of select="$result" />
    </xsl:template>

    <!-- ============================================================== -->
    <xsl:template name="Somme">  
        <xsl:param name="v1"/>  
        <xsl:param name="v2"/>
        
        <xsl:variable name="result"> 
            <xsl:choose>
                <xsl:when test="string($v1) = 'NaN' and string($v2) = 'NaN'"> 
                    <xsl:value-of select="$v1" />
                </xsl:when>
                <xsl:when test="string($v1) =   'NaN'"> 
                    <xsl:value-of select="$v2" />
                </xsl:when>
                <xsl:when test="string($v2) =   'NaN'"> 
                    <xsl:value-of select="$v1" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$v1 + $v2" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:copy-of select="$result" />
    </xsl:template>
        
    <!-- ============================================================== -->
    <xsl:template name="instancier-somme">  
        <xsl:param name="unNodeSet"  />  
        <xsl:param name="somme-courante" />
        
        <xsl:choose>
            <xsl:when test="$unNodeSet">
                <xsl:call-template name="instancier-somme">
                    <xsl:with-param name="unNodeSet" 
                                    select="$unNodeSet[position() > 1]"/>
                    
                    <xsl:with-param name="somme-courante">
                        <xsl:call-template name="Somme">
                            <xsl:with-param name="v1" 
                                            select="$somme-courante"/>
                        
                            <xsl:with-param name="v2">
                                <xsl:call-template name="valeur">
                                    <xsl:with-param name="unSingleton"
                                    select="$unNodeSet[position() = 1]"/>
                                </xsl:call-template>
                            </xsl:with-param>
                        </xsl:call-template>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            
            <xsl:otherwise>
                <xsl:value-of select="$somme-courante"  />
            </xsl:otherwise>
        </xsl:choose>
        
    </xsl:template>
    
    <!-- ============================================================== -->
    <xsl:template match="maison">
        <xsl:variable name="surface-habitable">
            <xsl:call-template name="instancier-somme">
                <xsl:with-param name="unNodeSet" select=".//*"/> 
                <xsl:with-param name="somme-courante" select="0"/>  
            </xsl:call-template>
        </xsl:variable>
        Maison id = <xsl:value-of select="@id"/>
        Surface habitable = <xsl:value-of select="$surface-habitable" />m2 />
    </xsl:template>
    
    <!-- ============================================================== -->
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
        Maison id = 1
        Surface habitable = 136m2 />
    
        Maison id = 2
        Surface habitable = 28m2 />
    
        Maison id = 3
        Surface habitable = 57.5m2 />
    

Fonction renvoyant plusieurs résultats

Motivation

Réalisation

Fonction renvoyant trois résultats x, y, z :
<xsl:template name="truc">
    <xsl:param name="..."/>
    
    <xsl:variable name="result">
        <x>
            <xsl:value-of select="..." />
        </x>
        <y>
            <xsl:value-of select="..." />
        </y>
        <z>
            <xsl:value-of select="..." />
        </z>
    </xsl:variable>
    
    <xsl:copy-of select="$result" />
    
</xsl:template>

<xsl:template match="...">

    <xsl:variable name="a">
        <xsl:call-template name="truc">
            <xsl:with-param name="..." select="..."/>
        </xsl:call-template>
    </xsl:variable>

    <xsl:value-of select="$a/x"/>
    <xsl:value-of select="$a/y"/>
    <xsl:value-of select="$a/z"/>

</xsl:template>

Exemple

separateurs.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output  method='text' encoding='ISO-8859-1' />
    
    <xsl:template name="index-of"> 
        <xsl:param name="aString"/> 
        <xsl:param name="testString"/>
        
        <xsl:variable name="result">
            <xsl:choose>
                <xsl:when test=" contains( $aString, $testString ) ">
                    <xsl:variable name="string-Before">
                        <xsl:value-of 
                        select="substring-before( $aString, $testString )" />
                    </xsl:variable>
                    <xsl:value-of select="1+string-length( $string-Before )" />
                </xsl:when>
                
                <xsl:otherwise>
                    -1
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        
        <xsl:copy-of select="$result" />
    </xsl:template>
    
    <xsl:template name="split-beforeAndAfter-firstSeparator"> 
    <!-- separator = apostrophe ou espace -->
        <xsl:param name="stringToSplit"/> 
        <!-- stringToSplit is space-normalized -->
        
        <xsl:variable name="indexOf-firstApos">
            <xsl:call-template name="index-of">
                <xsl:with-param name="aString" select="$stringToSplit"/>
                <xsl:with-param name="testString" select='"&apos;"'/>
            </xsl:call-template>
        </xsl:variable>
        
        <xsl:variable name="indexOf-firstSpace">
            <xsl:call-template name="index-of">
                <xsl:with-param name="aString" select="$stringToSplit"/>
                <xsl:with-param name="testString" select='" "'/>
            </xsl:call-template>
        </xsl:variable>
        
        <xsl:variable name="result">
            <xsl:choose>
                
            <!-- -->
            <xsl:when test=" $indexOf-firstApos &lt; $indexOf-firstSpace ">
                <before>
                    <xsl:value-of select="substring( $stringToSplit, 1, 
                                          $indexOf-firstApos - 1 )" />
                </before>
                <separator>'</separator>
                <after>
                    <xsl:value-of select="substring( $stringToSplit, 
                                          $indexOf-firstApos + 1 )" />
                </after>
            </xsl:when>
                
            <!-- -->
            <xsl:when test=" $indexOf-firstSpace &lt; $indexOf-firstApos ">
                <before>
                    <xsl:value-of select="substring(
                                              $stringToSplit, 1, 
                                              $indexOf-firstSpace - 1 )" />
                </before>
                <separator><xsl:text> </xsl:text></separator>
                <after>
                    <xsl:value-of select="substring(
                                              $stringToSplit, 
                                              $indexOf-firstSpace + 1 )" />
                </after>
            </xsl:when>
                
            </xsl:choose>
        </xsl:variable>
        
        <xsl:copy-of select="$result" />
        
    </xsl:template>
    
    <xsl:variable name="complainteDuCharretier">
        Pousser des charettes à longueur de journée réclame de l'énergie !
    </xsl:variable>
    
    <xsl:variable name="pendule">
        L'heure exacte.
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:variable name="split1">
            <xsl:call-template name="split-beforeAndAfter-firstSeparator">
                <xsl:with-param name="stringToSplit" 
                select="normalize-space($complainteDuCharretier)"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="normalize-space($complainteDuCharretier)"/>
        before = <xsl:value-of select="$split1/before"/>
        separator = "<xsl:value-of select="$split1/separator"/>"
        after = <xsl:value-of select="$split1/after"/>
        
        <xsl:variable name="split2">
            <xsl:call-template name="split-beforeAndAfter-firstSeparator">
                <xsl:with-param name="stringToSplit" 
                select="normalize-space($pendule)"/>
            </xsl:call-template>
        </xsl:variable>
        
        <xsl:text>
        
</xsl:text>
        <xsl:value-of select="normalize-space($pendule)"/>
        before = <xsl:value-of select="$split2/before"/>
        separator = "<xsl:value-of select="$split2/separator"/>"
        after = <xsl:value-of select="$split2/after"/>
    
    </xsl:template>
    
    <xsl:template match="text()"/>
    
</xsl:stylesheet>
Résultat
Pousser des charettes à longueur de journée réclame de l'énergie !
        before = Pousser
        separator = " "
        after = des charettes à longueur de journée réclame de l'énergie !
        
L'heure exacte.
        before = L
        separator = "'"
        after = heure exacte.

Utilisation d'une structure de données auxilaire

Motivation

Exemple

presentation.xml
<?xml version="1.0" encoding="UTF-16" ?>

<presentation>

    <pageDeTitre id="CoursXML.1" next="CoursXML.2">
        <titrePresentation>Comprendre XML et XSL</titrePresentation>
        <credit>
            <groupeAuteurs>
                <auteur>Philippe Drix</auteur>
                <societe>OBJECTIVA</societe>
            </groupeAuteurs>
        </credit>
    </pageDeTitre>


    <pageStandard id="CoursXML.2" prev="CoursXML.1" next="CoursXML.3">
        <titre level="1" id="CoursXML.2.Déroulement">
        Déroulement du Cours</titre>
        ...
    </pageStandard>
    
    
    <plan id="CoursXML.3" prev="CoursXML.2" next="CoursXML.4"/>


    <pageStandard id="CoursXML.4" prev="CoursXML.3" next="CoursXML.4.1">
        <titre level="1" id="CoursXML.4.__XMLgeneral"> 
        XML - Généralités </titre>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.4.1" prev="CoursXML.4" next="CoursXML.4.2">
        <titre level="2" id="CoursXML.4.1.__Langagesdebalisage"> 
        Les langages de balisage </titre>
        ...
        <bloc>
            <figure src="ArbreXML_1" id="fig:ArbreXML_1.CoursXML.4.1">
                <captionFigure>
                    Structure arborescente d'un document XML bien formé
                </captionFigure>
            </figure>
        </bloc>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.4.2" prev="CoursXML.4.1" next="CoursXML.5">
        <titre level="2" id="CoursXML.4.2.__AQuoiSertXML"> 
        A quoi peut servir XML ? </titre>
        
    </pageStandard>


    <pageStandard id="CoursXML.5" prev="CoursXML.4.2" next="CoursXML.6">
        <titre level="1" id="CoursXML.5.__StructuredocumentXML"> 
        Structure d'un document XML </titre>
        ...
        
        <bloc>
            Ces blocs juxtaposés ou imbriqués forment un arbre 
            <cfFigure cf="fig:ArbreXML_1.CoursXML.4.1"/> 
            comme tout document XML.
        </bloc>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.6" prev="CoursXML.5" next="CoursXML.7">
        <titre level="2" id="CoursXML.6.__DTD"> 
        Grammaire d'un document XML (DTD)
        </titre>
        ...
    </pageStandard>

    <pageFin id="CoursXML.7" prev="CoursXML.6" />

</presentation>
presentation-new.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<presentation>

    <pageDeTitre id="CoursXML.1" next="CoursXML.2">
        <titrePresentation>Comprendre XML et XSL</titrePresentation>
        <credit>
            <groupeAuteurs>
                <auteur>Philippe Drix</auteur>
                <societe>OBJECTIVA</societe>
            </groupeAuteurs>
        </credit>
    </pageDeTitre>


    <pageStandard id="CoursXML.2" prev="CoursXML.1" next="CoursXML.3">
        <titre level="1" id="CoursXML.2.__">
        Déroulement du Cours</titre>
        ...
    </pageStandard>
    
    
    <plan id="CoursXML.3" prev="CoursXML.2" next="CoursXML.4"/>


    <pageStandard id="CoursXML.4" prev="CoursXML.3" next="CoursXML.5">
        <titre level="1" id="CoursXML.4.__XMLgeneral"> 
        XML - Généralités </titre>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.5" prev="CoursXML.4" next="CoursXML.6">
        <titre level="2" id="CoursXML.5.__Langagesdebalisage"> 
        Les langages de balisage </titre>
        ...
        <bloc>
            <figure src="@src" id="fig:ArbreXML_1.CoursXML.5">
                <captionFigure>
                    Structure arborescente d'un document XML bien formé
                </captionFigure>
            </figure>
        </bloc>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.6" prev="CoursXML.5" next="CoursXML.7">
        <titre level="2" id="CoursXML.6.__AQuoiSertXML"> 
        A quoi peut servir XML ? </titre>
        
    </pageStandard>


    <pageStandard id="CoursXML.7" prev="CoursXML.6" next="CoursXML.8">
        <titre level="1" id="CoursXML.7.__StructuredocumentXML"> 
        Structure d'un document XML </titre>
        ...
        
        <bloc>
            Ces blocs juxtaposés ou imbriqués forment un arbre 
            <cfFigure cf="fig:ArbreXML_1.CoursXML.5"/> 
            comme tout document XML.
        </bloc>
        ...
    </pageStandard>


    <pageStandard id="CoursXML.8" prev="CoursXML.7" next="CoursXML.9">
        <titre level="2" id="CoursXML.8.__DTD"> 
        Grammaire d'un document XML (DTD)
        </titre>
        ...
    </pageStandard>

    <pageFin id="CoursXML.7" prev="CoursXML.6"/>
    

</presentation>
Constitution de la table de correspondance
<xsl:variable name="lesPages">
    <pages>
    <xsl:for-each select="/presentation/*">
        <page>
        <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
        <xsl:attribute name="newId">
            <xsl:value-of select="position()"/>
        </xsl:attribute>
        </page>
    </xsl:for-each>     
    </pages>
</xsl:variable>
Arbre généré lors de l'évaluation de cette variable
<pages>
    <page id="CoursXML.1"   newId="1"/>
    <page id="CoursXML.2"   newId="2"/>
    <page id="CoursXML.3"   newId="3"/>
    <page id="CoursXML.4"   newId="4"/>
    <page id="CoursXML.4.1" newId="5"/>
    <page id="CoursXML.4.2" newId="6"/>
    <page id="CoursXML.5"   newId="7"/>
    <page id="CoursXML.6"   newId="8"/>
    <page id="CoursXML.7"   newId="9"/>
<pages>
Arbre généré lors de l'évaluation de cette variable
<xsl:template name='instancier-newID'>  
    <xsl:param name='oldId'/>
    <xsl:value-of select="$lesPages/pages/page[
                              attribute::id = $oldId
                          ]/attribute::newId" />
</xsl:template>
Modification des attributs d'une pageStandard

<xsl:template match='pageStandard'>  
    <pageStandard>
        <xsl:variable name="NoSeq">
            <xsl:call-template name='instancier-newID'> 
                <xsl:with-param name='oldId' select="@id" />
            </xsl:call-template>
        </xsl:variable>
        
        <xsl:attribute name="id">CoursXML.<xsl:value-of select="$NoSeq" />
        </xsl:attribute>
        <xsl:attribute name="prev">CoursXML.<xsl:value-of select="$NoSeq - 1"/>
        </xsl:attribute>
        <xsl:attribute name="next">CoursXML.<xsl:value-of select="$NoSeq + 1"/>
        </xsl:attribute>
        ...  
    </pageStandard>
</xsl:template>
Modification des attributs d'une pageStandard ou d'un plan
<xsl:template match='pageStandard | plan'>   
    <xsl:element name="{local-name(.)}">  
        <xsl:variable name="NoSeq">
            <xsl:call-template name='instancier-newID'> 
                <xsl:with-param name='oldId' select="@id" />
            </xsl:call-template>
        </xsl:variable>
        
        <xsl:attribute name="id">CoursXML.<xsl:value-of select="$NoSeq" />
        </xsl:attribute>
        <xsl:attribute name="prev">CoursXML.<xsl:value-of select="$NoSeq - 1"/>
        </xsl:attribute>
        <xsl:attribute name="next">CoursXML.<xsl:value-of select="$NoSeq + 1"/>
        </xsl:attribute>
        
        <xsl:apply-templates/>  
    </xsl:element>
</xsl:template>
Modification des attributs d'une pageDeTitre
<xsl:template match='pageDeTitre'>   
    <!-- idem sans l'attribut prev -->
</xsl:template>
Modification des attributs d'une pageFin
<xsl:template match='pageDeTitre'>   
    <!-- idem sans l'attribut next -->
</xsl:template>
Mise en place de règles spécifiques pour les enfants d'une page
<xsl:template match='titre'>
    <titre>
        ...
    </titre>
</xsl:template>

<xsl:template match='figure'>
    <figure src="@src">
        ...
    </figure>
</xsl:template>

<xsl:template match='cfFigure'>
    <cfFigure>
        ...
    </cfFigure>
</xsl:template>
Règle de recopie ramasse-tout
<xsl:template match='*'> 
    <xsl:copy>
        <xsl:apply-templates select='@*|node()' />
    </xsl:copy>
</xsl:template>
renumeroter.xsl
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet 
    xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" 
    version   = "1.1"> <!-- compatibilité Saxon 6.5 -->

 
    <xsl:output  method='xml' encoding='ISO-8859-1' />

    <xsl:variable name="lesPages">
        <pages>
        <xsl:for-each select="/presentation/*">
            <page>
            <xsl:attribute name="id"><xsl:value-of select="@id"/>
            </xsl:attribute>
            <xsl:attribute name="newId"><xsl:value-of select="position()"/>
            </xsl:attribute>
            </page>
        </xsl:for-each>     
        </pages>
        
    </xsl:variable>
    
    
    <xsl:template name='instancier-newID'>  
        <xsl:param name='oldId'/>
        <xsl:value-of select="$lesPages/pages/page[
                                  attribute::id = $oldId
                              ]/attribute::newId" />
    </xsl:template>
    
    <xsl:template match='pageStandard | plan'>  
        <xsl:element name="{local-name(.)}">
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' select="@id" />
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:attribute name="id">CoursXML.<xsl:value-of 
                                            select="$NoSeq" /></xsl:attribute>
            <xsl:attribute name="prev">CoursXML.<xsl:value-of 
                                         select="$NoSeq - 1"/></xsl:attribute>
            <xsl:attribute name="next">CoursXML.<xsl:value-of 
                                        select="$NoSeq + 1" /></xsl:attribute>
            
            <xsl:apply-templates/>  
        </xsl:element>
    </xsl:template>
    
    <xsl:template match='pageDeTitre'>  
        <xsl:element name="{local-name(.)}">
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' select="@id" />
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:attribute name="id">CoursXML.<xsl:value-of 
                                             select="$NoSeq" /></xsl:attribute>
            <xsl:attribute name="next">CoursXML.<xsl:value-of 
                                         select="$NoSeq + 1" /></xsl:attribute>
            
            <xsl:apply-templates/>  
        </xsl:element>
    </xsl:template>
    
    <xsl:template match='pageFin'>  
        <xsl:element name="{local-name(.)}">
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' select="@id" />
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:attribute name="id">CoursXML.<xsl:value-of 
                                             select="$NoSeq" /></xsl:attribute>
            <xsl:attribute name="prev">CoursXML.<xsl:value-of 
                                         select="$NoSeq - 1" /></xsl:attribute>
            
        </xsl:element>
    </xsl:template>
    
    <xsl:template match='titre'>
        
        <titre>
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' 
                                 select="parent::pageStandard/attribute::id" />
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:variable name="texteID" select='substring-after(@id, ".__")'/>
            
            <xsl:attribute name="level">
                <xsl:value-of select="@level" />
            </xsl:attribute>
            
            <xsl:attribute name="id">
                <xsl:text>CoursXML.</xsl:text>
                <xsl:value-of select="$NoSeq" />
                <xsl:text>.__</xsl:text>
                <xsl:value-of select="$texteID" />
            </xsl:attribute>
            
            <xsl:apply-templates/>
            
        </titre>
    </xsl:template>
    
    
    <xsl:template match='figure'>
        
        <figure src="@src">
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' 
                                select="ancestor::pageStandard/attribute::id"/>
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:variable name="texteID" select='substring-before(@id, ".")' />
            
            <xsl:attribute name="id">
                <xsl:value-of select="$texteID" />
                <xsl:text>.CoursXML.</xsl:text>
                <xsl:value-of select="$NoSeq" />
            </xsl:attribute>
            
            <xsl:apply-templates/>
        </figure>
    </xsl:template>
    
    
    <xsl:template match='cfFigure'>
        
        <cfFigure>
            <xsl:variable name="NoSeq">
                <xsl:call-template name='instancier-newID'> 
                    <xsl:with-param name='oldId' 
                                           select="substring-after(@cf, '.')"/>
                </xsl:call-template>
            </xsl:variable>
            
            <xsl:variable name="texteID" select='substring-before(@cf, ".")' />
            
            <xsl:attribute name="cf">
                <xsl:value-of select="$texteID" />
                <xsl:text>.CoursXML.</xsl:text>
                <xsl:value-of select="$NoSeq" />
            </xsl:attribute>
        </cfFigure>
    </xsl:template>
    
    
    <xsl:template match='*'> 
        <xsl:copy>
            <xsl:apply-templates select='@*|node()' />
        </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>
Modifications pour utiliser la fonction d'extension nodeset
<xsl:stylesheet 
    xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"  
    xmlns:xalan="http://xml.apache.org/xalan"
    exclude-result-prefixes="xalan"
    version   = "1.0">
        
    <xsl:template name='instancier-newID'>  
        <xsl:param name='oldId/>
        <xsl:value-of select="xalan:nodeset($lesPages)/pages/page[
                                   attribute::id = $oldId]/attribute::newId" />
    </xsl:template>

Identité de nœuds et node-set de valeurs toutes différentes

Motivation

Node-set des surfaces
<xsl:variable name="x" select="/maison/RDC/*/attribute::surface"/>

Réalisation

Tests d'identité

Tests d'identité
count( $NS1 | $NS2 ) = 1
generate-id( $NS1 ) = generate-id( $NS2 )
Tests d'appartenance
count( $N | $NS ) = count( $NS )

Node-set de valeurs toutes différentes

Méthode du preceding-sibling
/maison/RDC/*/attribute::surface[
    la valeur textuelle de . n'existe pas déjà dans le node-set en construction
]
/maison/RDC/*/attribute::surface[
    la valeur textuelle de . n'existe pas déjà dans un certain node-set NS
]
/maison/RDC/*[ la valeur textuelle de ./attribute::surface n'existe pas déjà dans preceding-sibling::*/attribute::surface ]/attribute::surface
Création d'un node-set de surfaces toutes différentes
/maison/RDC/*[ not( ./@surface = preceding-sibling::*/@surface ) ]/@surface
Méthode du regroupement par clé
<xsl:key name="groupesdeSurfacesParValeurs" 
         match="attribute::surface" 
         use="." />
//attribute::surface
key('groupesdeSurfacesParValeurs', la valeur textuelle du nœud testé)
key('groupesdeSurfacesParValeurs', .)
key('groupesdeSurfacesParValeurs', .)[1]
//attribute::surface[ 
    . s'identifie à ( key('groupesdeSurfacesParValeurs', .)[1] )
]
Création d'un node-set de surfaces toutes différentes
//attribute::surface[
      generate-id() = 
      generate-id( 
         key('groupesdeSurfacesParValeurs', .)[1]
      )
  ]
preceding-sibling::*[1] Table following-sibling::*[1]