EPITA
XML et les services Web

Jean-François Perrot

XSLT
eXtensible Stylesheet Language Transform

(révision du 8 janvier 2014)
  1. I. Origines, mise en œuvre
    1. Un langage pour des feuilles de style
    2. Mise en œuvre des feuilles de style
      1. Par une "instruction de traitement"
      2. Via un interpréteur explicite
      3. Par un programme Java
    3. Modus operandi
      1. Rôle de la balise <xsl:output>
      2. Documents multiples

  2. II. Premiers exemples
    1. Visualisation d'un fichier de noms et de notes dans un navigateur :
    2. Commentaire
    3. Première variation : attributs
    4. Deuxième variation : liste
    5. Troisième variation : attributs + liste

  3. III. Vue d'ensemble de la mécanique
    1. L'unité de base de la programmation XSLT est la règle.
    2. Mécanisme récursif
    3. Application d'une règle à un sommet (nœud) de l'arbre XML
    4. Retour sur les règles implicites (ou "par défaut" : default rules)
    5. Priorité des règles et résolution des conflits

  4. IV. Rudiments de XPath
    1. XPath
    2. Chemins simples
    3. Les prédicats
    4. Les fonctions
    5. Un exemple très "international".
      1. Le fichier XML : ExInter.xml
      2. Le spectacle produit :
      3. La feuille de style : list.xsl

  5. V. Produire du XML
    1. Principe
      1. Une éventuelle sérialisation
      2. Attention : une déclaration de DTD se règle à ce niveau
    2. Espaces de noms
      1. Les espaces de noms se déclarent dans la balise-racine de la feuille de style.
      2. Choix des préfixes associés aux différents espaces de noms dans un  feuille de style.
      3. Exemples
    3. Les constructions de base "à la DOM"
    4. Recopie d'un sous-arbre

  6. VI. Structures de contrôle, règles paramétrées, règles nommées
    1. Pilotage des règles par <xsl:sort.../>
    2. Pilotage des règles par mode
    3. Structures de contrôle
    4. Appels récursifs explicites
    5. Règles paramétrées
    6. L'exemple de la génération des histogrammes

  7. VII. Un exemple (relativement) complet
    1. But
    2. Principe
    3. La feuille de style finale HtmlMIL.xsl
    4. La feuille de style principale PrettyMIL.xsl
    5. La feuille spéciale pour les expressions ExpArBoolMIL2ap.xsl

I. Origines, mise en œuvre

  1. Un langage pour des feuilles de style

    separation of concerns : séparer la structure de la présentation

  2. Mise en œuvre des feuilles de style

  3. Modus operandi

II. Premiers exemples

  1. Visualisation d'un fichier de noms et de notes dans un navigateur :

    à partir du fichier XML [Nom_note_2.xml] :

    <?xml version="1.0" ?>
    <liste>
        <eleve>
            <nom> Toto</nom> <note> 12 </note>
        </eleve>
        <eleve>
            <nom> Tata</nom> <note> 13 </note>
        </eleve>
        <eleve>
            <nom> Tutu</nom> <note> 17 </note>
        </eleve>
        <eleve>
            <nom> Titi</nom> <note> 11 </note>
        </eleve>
    </liste>


    on veut obtenir ce spectacle :

    Table

    Pour cela, il faut transformer l'arbre XML en un texte HTML interprétable par le navigateur :

    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Visualisation</title>
    </head>
    <body>
        <h2> Voici le tableau des noms et des notes</h2>
        <table border="2" bgcolor="yellow">
            <tr>
                <th>Nom</th>
                <th>Note</th>
            </tr>
            <tr>
                <td> Toto</td>
                <td> 12 </td>
            </tr>
            <tr>
                <td> Tata</td>
                <td> 13 </td>
            </tr>
            <tr>
                <td> Tutu</td>
                <td> 17 </td>
            </tr>
            <tr>
                <td> Titi</td>
                <td> 11 </td>
            </tr>
        </table>
    </body>
    </html>


    C'est l'objet de la feuille de style XSLT que voici [table.xsl]:


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

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

    <xsl:template match="/">
        <html><head><title>Visualisation</title></head>
            <body>
                <h2> Voici le tableau des noms et des notes</h2>
                <table border="2" bgcolor="yellow">
                    <tr>
                        <th>Nom</th>
                        <th>Note</th>
                    </tr>
                    <xsl:apply-templates/>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="liste/eleve">
        <tr>
            <td><xsl:value-of select="nom"/></td>
            <td><xsl:value-of select="note"/></td>
        </tr>
    </xsl:template>

    </xsl:stylesheet>

  2. Commentaire

    La feuille de style est elle-même un fichier XML, qui fait appel à l'espace de noms
    "http://www.w3.org/1999/XSL/Transform".

    Les éléments <xsl:template> représentent des règles,
    dont l'application à un fichier XML a pour effet d'engendrer le texte en sortie.
    Leur attribut "match" détermine leur domaine d'application.

    La première s'applique au document XML tout entier (symbolisé par "/").
    Elle a  pour effet
    La seconde s'applique aux éléments de nom <eleve> qui sont fils de <liste>.
    Pour chacun d'entre eux, elle engendre une ligne <tr> composée de deux celleules <td>
    contenant la "valeur" des éléments-fils <nom> et <note> respectivement.
  3. Première variation : attributs

    Pour obtenir le même résultat à partir d'un fichier XML où les noms et les notes
    sont représentés par des attributs et non par les éléments-fils (cf. la discussion dans le Cours n° 1),
    comme ceci [Nom_note_1.xml]:

    <?xml version="1.0" ?>
    <liste>
    <eleve nom="Toto" note="12"/>
    <eleve nom="Tata" note="13"/>
    <eleve nom="Tutu" note="17"/>
    <eleve nom="Tutu" note="11"/>
    </liste>

    il suffit de modifier la seconde règle ainsi [tableAt.xsl]:

    <xsl:template match="liste/eleve">
        <tr>
            <td><xsl:value-of select="@nom"/></td>
            <td><xsl:value-of select="@note"/></td>
        </tr>
    </xsl:template>


    La présence de l'arrobas dans select="@nom" et dans select="@note" 
    indique qu'on s'intéresse aux attributs de l'élément considéré et non pas à ses enfants.

    Notons que <xsl:value-of> renvoie
  4. Deuxième variation : liste

    On veut maintenant afficher ceci, à partir de notre premier fichier XML.

    Liste

    En raisonnant par analogie, on voit que la feuille de style suivante devrait faire l'affaire [list.xsl] :


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


    <xsl:template match="/">
      <html><head><title>Visualisation</title></head>
      <body>
       <h2> Voici la liste des noms et des notes</h2>
        <ol>
           <xsl:apply-templates/>
        </ol>
       </body>
      </html>
    </xsl:template>

    <xsl:template match="liste/eleve">
        <li>
            <xsl:value-of select="nom"/> :
            <xsl:value-of select="note"/>
        </li>
    </xsl:template>

    </xsl:stylesheet>

  5. Troisième variation : attributs + liste

    Vous en savez assez pour rédiger une feuille de style propre à afficher en mode liste le fichier Nom_note_1.xml,
    et, toujours par analogie, pour en rédiger deux autre pour afficher le fichier ex.xml :
    <?xml version="1.0"?>
    <liste>
    <eleve nom='Pierre'> <note> 12 </note>
    </eleve>
    <eleve nom="Paul"> <note> 13 </note>
    </eleve>
    <eleve nom='Jacques'> <note>17 </note>
    </eleve>
    </liste>
    en mode table et en mode liste.

III. Vue d'ensemble de la mécanique

  1. L'unité de base de la programmation XSLT est la règle.

  2. Mécanisme récursif

  3. Application d'une règle à un sommet (nœud) de l'arbre XML

    C'est par rapport à ce sommet que sont évaluées les expressions qui apparaissent
    dans les attributs select (exemples ci-dessus) et test (voir plus loin) du corps de la règle.

    Prenons quelques exemples :

    1. avec la seconde règle de table.xsl :

      <xsl:template match="liste/eleve">
          <tr>
              <td><xsl:value-of select="nom"/></td>
              <td><xsl:value-of select="note"/></td>
          </tr>
      </xsl:template>

      appliquée au sommet (élément) de Nom_note_2.xml :

         <eleve>
          <nom> Toto</nom> <note> 12 </note>
         </eleve>

      La chaîne "nom" dans select="nom" s'évalue comme
      le premier sommet-fils de l'eleve en question dont la balise est "nom",
      et l'opérateur xsl:value-of renvoie la chaîne " Toto".

      De même, la chaîne "note" dans select="note" s'évalue comme
      le premier sommet-fils de l'eleve en question dont la balise est "note",
      et l'opérateur xsl:value-of renvoie la chaîne " 12 ".

      D'où pour l'application de la règle une contribution totale en sortie :
          <tr>
              <td> Toto</td>
              <td> 12 </td>
          </tr>


      Après quoi on passe au prochain sommet à traiter, qui en l'occurrence sera
      le suivant dans l'arbre XML, à savoir
        <eleve>
          <nom> Tata</nom> <note> 13 </note>
        </eleve>


    2. Idem, avec la seconde règle de tableAt.xsl (1ère variation ci-dessus),
      appliquée à l'élément homologue de Nom_note_1.xml :

          <eleve nom="Toto" note="12"/>

      La chaîne "@nom" dans select="@nom" s'évalue comme
      l'unique attribut de l'eleve en question dont la clef est "nom",
      et l'opérateur xsl:value-of renvoie la chaîne "Toto".

      De même, la chaîne "@note" dans select="@note" s'évalue comme
      l'unique attribut de l'eleve en question dont la clef est "note",
      et l'opérateur xsl:value-of renvoie la chaîne "12".

      Au total on obtient la même contribution en sortie
      (à quelques blancs près, qui sont sans importance pour le navigateur destinataire).
      après quoi on passe au suivant :
          <eleve nom="Tata" note="13"></eleve>


    3. Revenons au début du calcul : l'unique sommet-candidat est le Document.
      Dans nos exemples, la seule règle applicable est donc la première (match = "/").
      Elle engendre d'abord du texte en sortie, à savoir

      <html>
         <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

            <title>Visualisation</title>
         </head>
         <body>
            <h2> Voici le tableau des noms et des notes</h2>
            <table border="2" bgcolor="yellow">
               <tr>
                 <th>Nom</th>
                 <th>Note</th>
               </tr>

      Après quoi, l'exécution de <xsl:apply-templates /> provoque l'activation du mécanisme récursif :
      la liste des sommets-enfants du sommet courant est empilée sur la pile des sommets-candidats,
      et on passe au premier de cette liste (c-à-d. on essaie toutes les règles sur ce sommet).

    4. Continuons : le sommet initial Document a un seul fils <liste>.
      Aucune règle ne lui est applicable : la première ne s'applique qu'au Document,
      la seconde demande un element <eleve> qui soit fils immédiat d'un element <liste>.
      Par conséquent le mécanisme récursif s'enclenche et tous les enfants de <liste> sont empilés.
      Comme <liste> n'a pas de contenu textuel, rien n'est envoyé en sortie.

    5. Poursuivons : le premier sommet-candidat est celui que nous avons envisagé en i. ci-dessus, à savoir
         <eleve>
          <nom> Toto</nom> <note> 12 </note>
         </eleve>

      La seconde règle est (seule) applicable, puisqu'il s'agit bien d'un <eleve> qui est fils immédiat d'une <liste>.
      Ce qui se produit alors a été décrit en i.

      Après quoi on passe au deuxième fils de <liste>, etc jusqu'à épuisement de la liste.

    6. Achevons : après que le dernier fils de <liste> ait été traité, la pile de sommets créée par le
      <xsl:apply-templates /> de la première règle est épuisée,
      on revient donc à l'exécution de ladite première règle, en séquence, et on envoie en sortie les balises HTML fermantes :
      </table>
      </body>
      </html>
      et le calcul se termine.

    7. On obtiendra exactement le même résultat qu'avec la feuille de style table.xsl vue ci-dessus
      • en donnant comme filtre à la première règle : match="/list" au lieu de match= "/"
      • en donnant comme filtre à la seconde règle : match="/list/eleve" ou bien match="eleve"
        au lieu de match="list/eleve"
      • et de même pour les variations pour attributs et pour structure mixte (genre ex.xml).

  4. Retour sur les règles implicites (ou "par défaut" : default rules)

  5. Priorité des règles et résolution des conflits

    1. Il arrive fréquemment - ne serait-ce qu'en raison des la présence des règles implicites - que plusieurs règles puissent s'appliquer
      à un même élément. En ce cas, l'interprète XSLT en choisira une seule et il l'appliquera.
      Le choix de l'interprète repose sur une notion de priorité : la règle choisie sera celle qui a la plus haute priorité
      parmi les règles applicables.

      L'attribution de priorité aux règles se fait selon deux mécanismes : explicite et implicite.
      Il ne faut pas hésiter à recourir au mécanisme explicite, car le mécanisme implicite est fort imparfait et peut conduire à de graves déconvenues. En effet, aux termes de la documentation officielle,
      It is an error if this [le processus d'attribution de priorité] leaves more than one matching template rule. An XSLT processor may signal the error; if it does not signal the error, it must recover by choosing, from amongst the matching template rules that are left, the one that occurs last in the stylesheet.
      Comme la règle générale veut que l'ordre d'écriture des templates soit sans importance, on imagine les conséquences désastreuses que peut avoir cette clause. Car bien entendu les processeurs ne signalent jamais l'erreur en question !

    2. Priorité explicite : au moyen de l'attribut priority, à valeur numérique :
             <xsl:template match="monElem" priority="2">...  aura précédence sur
             <xsl:template match="monElem" priority="1">...

      La valeur de cet attribut peut être un nombre "flottant" (écrit avec un point), comme 0.6, et même négatif.
      Les priorités implicites vont de -0.5 à 0.5.

    3. Priorité implicite : alias default priority

      • Les règles exprimées (explicitement) sont prioritaires par rapport aux règles implicites (default rules).
        Il suffit donc d'écrire une règle pour que les règles implicites qui lui feraient concurrence soient rejetées.

      • Il y a exactement quatre niveaux de priorités implicites, par ordre de priorité croissante :
        1. filtres anonymes sans préfixe de namespace, comme "*", ou "text()" : priorité négative = -0.5 ;
        2. filtres anonymes avec préfixe de namespace, comme "monpréfixe:*" : priorité négative = -0.25 ;
        3. filtres comportant seulement un nom, avec ou sans préfixe de namespace, comme "toto" ou "monpréfixe:toto" :
          priorité nulle = 0 ;
        4. tout autre filtre, c'est-à-dire dès qu'aparaît un cheminement '/' ou un prédicat entre '[' et ']' (voir ci-après) :
          priorité positive = 0.5 .

      • Commentaire :
        Le principe selon lequel le plus spécifique a précédence sur le plus général est souvent invoqué en informatique,
        notamment en programmation par objets, mail il n'est pas facile de le réconcilier avec ces quatre niveaux,
        tout spécialement avec le quatrième.
        On peut admettre qu'un filtre comme "père/fils" est plus spécifique que "fils", puisqu'il ne sélectionne que certains sommets "fils",
        mais il est plus difficile d'accepter que "père/fils[@âge='25']" ait la même priorité que "//fils".

        On aimerait bien pouvoir dire quelque chose comme :

        soit un élément <monElem> susceptible de recevoir un attribut (optionnel) monAttr.
        On peut écrire à son intention trois types de règles, de généralité décroissante et de priorité croissante :
        1. <xsl:template match="monElem">...
          qui s'applique aussi bien en présence qu'en l'absence de l'attribut
        2. <xsl:template match="monElem[@monAttr]">... [sur cette écriture, voyez plus loin]
          qui ne s'applique que si  l'attribut monAttr est présent, indépendamment de sa valeur
        3.  <xsl:template match="monElem[@monAttr='maChose']">...
          qui ne s'applique que si  l'attribut monAttr est présent et a pour valeur "maChose".

        mais ce serait une grave erreur !

        Hélas, il faut se convaincre que le principe en question n'est applicable que dans des cas très particuliers (comme celui de l'héritage en PPO),
        et qu'ici il ne l'est pas du tout !
        Par exemple, son application systématique exigerait que l'on puisse comparer deux prédicats et décider si l'un est plus général que l'autre...

    4. Exemple :  pour obtenir l'affichage que voici (avec des lignes horizontales séparatrices, sauf en dernière position)

      ListHR

      les deux feuilles de style listHR1.xsl et listHR2.xsl sont en pratique équivalentes... mais pourquoi donc ?

      1. La première emploie deux règles dont les filtres sont exclusifs l'un de l'autre
        <xsl:template match="liste/eleve[position() != last()]"> <!-- tous sauf le dernier -->
        <xsl:template match="liste/eleve[position() = last()]"> <!-- rien que le dernier -->

        il n'y a donc pas de problème de priorité.

      2. La seconde lève la restriction sur le premier filtre,
        <xsl:template match="liste/eleve"> (supposée plus générale, donc moins prioritaire)
        et conserve la deuxième règle.

        Or en fait les deux règles ont alors la même priorité !!!
        Toutefois, le résultat obtenu est satisfaisant... parce que le conflit n'a lieu que sur la dernière ligne du fichier,
        et que le processeur, au lieu de signaler l'erreur, met alors en œuvre la stratégie de secours,
        et applique donc la règle qui apparaît en dernière position dans la feuille de style...
        qui se trouve être la bonne !

        Il suffit d'intervertir les deux règles pour que le résultat change, ce qui est inadmissible.

      3. Conclusion : si on veut jouer sur les priorités, il est indispensable de marquer explicitement la règle prioritaire :

        <xsl:template match="liste/eleve[position() = last()]" priority="1">

        Une autre solution, moins sécuritaire, est de modifier l'écriture de la règle-racine
        pour faire intervenir la distinction entre les niveaux 3 et 4 :

        <xsl:template match="/">
          <html><head><title>Visualisation</title></head>
          .....
               <xsl:apply-templates select="liste/*" />
          .....


        <xsl:template match="eleve"> (à présent, de priorité nulle)
        <xsl:template match="eleve[position() = last()]"> (de priorité 0.5)



IV. Rudiments de XPath

  1. XPath

    Les expressions qui figurent comme valeurs des attributs match, select ou test sont rédigées
    dans un langage particulier appelé XPath, dont la syntaxe n'est pas du XML.
    Ce langage a pour but de décrire des ensembles de chemins dans un arbre XML,
    et par suite des ensembles de sommets (ceux qu'on peut atteindre en suivant ces chemins).
    Nous n'en donnerons ici que le strict nécessaire à nos besoins.
    Pour en savoir davantage, voyez Wikipedia (en anglais), ou l'excellent chapitre 9 du livre XML in a Nutshell.

    Il faut prendre garde que nous décrivons ci-dessous les différentes formes d'expressions courantes
    sans distinguer celles qui sont autorisées dans un filtre match de celles qui sont acceptées dans un select
    ou dans un test. Rien ne remplace l'expérience !

    Tous les exemples dans les paragraphes 2, 3, 4 suivants seront pris sur le fichier qr.xml 
    qui décrit l'arbre syntaxique d'un petit programme, et qui fournira plus loin la base d'un de nos exemples.
    Il est suffisamment compliqué pour mettre en œuvre quelques ressources de XPath !

    <?xml version='1.0'?>

    <Prog>
        <Variables> <Var nom="a"/> <Var nom="b"/> <Var nom="q"/> <Var nom="r"/>
        </Variables>
        <Sequence>
            <Lecture> <Var nom="a"/> </Lecture>
            <Lecture> <Var nom="b"/> </Lecture>
            <Affectation> <Var nom="q"/> <Cte val="0"/> </Affectation>
            <Affectation> <Var nom="r"/> <VarExp> <Var nom="a"/> </VarExp>
            </Affectation>
            <Boucle>
                <Comparaison op="&gt;=">
                    <VarExp> <Var nom="t"/> </VarExp>
                    <VarExp> <Var nom="b"/> </VarExp>
                </Comparaison>
                <Sequence>
                    <Affectation> <Var nom="q"/>
                        <Bin op="+">
                          <VarExp> <Var nom="q"/> </VarExp>
                          <Cte val="1"/>
                        </Bin>
                    /Affectation>
                    <Affectation> <Var nom="r"/>
                        <Bin op="-">
                          <VarExp><Var nom="r"/></VarExp>
                          <VarExp><Var nom="b"/></VarExp>
                        </Bin>
                    </Affectation>
                </Sequence>
            </Boucle>
            <Ecriture> <VarExp> <Var nom="q"/> </VarExp> </Ecriture>
            <Ecriture> <VarExp> <Var nom="r"/> </VarExp> </Ecriture>
        </Sequence>
    </Prog>


  2. Chemins simples

    L'expression de base en XPath désigne un chemin par la suite des noms des nœuds qui le composent,
    séparés par des obliques "/", à la manière exacte des chemins dans l'arbre des répertoires en Unix.

    Il faut bien distinguer

    Exemples :
    1. /Prog/Sequence/Lecture/Var :
      chemin absolu qui désigne les deux variables des deux instructions de lecture au début du programme
      (dont les noms sont "a" et "b").
    2. Sequence/Affectation/Var
      chemin relatif qui désigne deux variables (dont les noms sont "q" et "r"),
      soit à partir de /Prog/Sequence, soit à partir de /Prog/Sequence/Boucle/Sequence

    Comme en Unix,

    À la différence de Unix, on peut terminer un chemin en demandant non pas un sommet-enfant
    mais un attribut :
    1. /Prog/Sequence/Lecture/Var/@nom :
      chemin absolu qui désigne les noms des deux variables des deux instructions de lecture
      au début du programme (à savoir "a" et "b").
    2. Sequence/Affectation/Var/@nom
      chemin relatif qui désigne les noms de deux variables (en l'occurrence, "q" et "r"),

    En outre,
    Ce qui permet de formuler les deux règles implicites dont nous avons parlé plus haut :

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

    et
    <xsl:template match="text() | @*">
      <xsl:value-of select="." />
    </xsl:template>
  3. Les prédicats

    On peut insérer dans un chemin des conditions portant sur les sommets du rang considéré.
    Ces conditions s'expriment par des expressions booléennes appelées prédicats (référence à la logique),
    qui sont placées entre crochets.
    Plusieurs prédicats peuvent se suivre, chacun restreignant le choix effectué par le précédent.

    N.B. dans ces prédicats, il est fait grand usage de l'égalité qui s'écrit "=" et non "==" comme en Java,
    et de la différence qui s'écrit "!=" comme en Java.
    Les symboles des inégalités numériques "<", ">", "<=" et ">=" posent évidemment un problème typographique
    dans le contexte d'un langage à balises.
    En XSLT il faut les écrire avec des "entités" : "&lt;", "&gt;", "&lt;=" et "&gt;=".

    Exemples :
    1. /Prog/Sequence/Lecture/Var[@nom = 'a']
      sélectionne la variable de la première des deux instructions de lecture au début du programme

    2. Sequence/Affectation/Bin[@op = '+']/VarExp/Var
      sélectionne la variable en partie gauche de la première des deux affectations de la boucle

    3. Sequence/Affectation/Var[@nom = 'q']/../Bin
      choisit l'expression arithmétique en partie droite de l'affectation dont la variable en partie gauche
      s'appelle "q" (il n'y en a qu'une).

    Outre les tests classiques d'égalité ou d'inégalité de valeurs, les prédicats de XPath peuvent aussi vérifier la présence
    d'un sommet donné ou d'un attribut nommé, ou l'existence de sommets ou d'attributs.
    Par exemple, l'expression "monElem[@monAttr]"choisira tous les élément monElem pour lesquels l'attribut (optionnel) monAttr est présent,
    par opposition à "monElem[@monAttr='maChose']" qui ne choisira que ceux pour lesquels l'attribut en question vaut 'maChose'.
    De même, l'expression "monElem[@*]"choisira tous les élément monElem pour lesquels au moins un attribut est présent.

    Enfin, les opérations booléennes traditionnelles sont disponibles, avec pour symboles and, or et not().

  4. Les fonctions

    XPath possède une panoplie de fonctions standard diverses dont voici les plus importantes :
    Il y a aussi un jeu complet d'opérateurs arithmétiques et booléens (ces derniers s'écrivant and, or, not).

    Avec ces fonctions, on peut composer des prédicats élaborés et calculer rapidement
    des résultats intéressants.

    Exemples :
    Autres exemples : revoyez l'exemple de priorités implicites.

    En voici un de plus, qui utilise la fonction sum (applicable à des nombres) :
    pour obtenir la moyenne des notes de Nom_note_2.xml , il suffit de demander

           <xsl:value-of select="sum(/liste/eleve/note) div count(/liste/eleve)" />

    Note sur la négation et l'absence.
  5. Un exemple très "international".

    1. Le fichier XML : ExInter.xml

      <?xml version="1.0"?>
      <Capitales>
          <France>Paris</France>
          <España>Madrid</España>
          <Česko>Praha</Česko>
          <Україна>Київ</Україна>
          <България>София</България>
          <Ελλάς>Αθήνα</Ελλάς>
          <Россия>Москва</Россия>
          <საქართველო>თბილისი</საქართველო>
          <Հայաստան>Երեւան</Հայաստան>
          <!-- <ኢትዮጵያ>አዲስ አበባ</ኢትዮጵያ> -->
          <भारत>नई दिल्ली</भारत>
          <தமிழ்_நாடு>ென்னை</தமிழ்_நாடு>
          <ประเทศไทย>กรุงเทพฯ</ประเทศไทย>
          <Việt_Nam>Hà Nội</Việt_Nam>
          <中華>北京</中華>
          <日本国>東京</日本国>
      </Capitales>

    2. Le spectacle produit :

      Capitales

    3. La feuille de style : list.xsl
      Même structure d'ensemble que table.xsl et al,
      emploi de la fonction name() et des symboles "*" (tous les enfants) et "." (le sommet courant).

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

          <xsl:output method="html" encoding="utf-8" />

          <xsl:template match="/">
              <html><head><title>Visualisation</title></head>
              <body>
                  <h2> Voici une liste de pays avec leurs capitales</h2>
                  <ul>
                      <xsl:apply-templates/>
                  </ul>
              </body>
              </html>
          </xsl:template>

          <xsl:template match="Capitales/*">
              <li>
                  <xsl:value-of select="name()"/> :
                  <xsl:value-of select="."/>
                  <!-- le contenu textuel du sommet courant -->
              </li>
          </xsl:template>

      </xsl:stylesheet>


V. Produire du XML

  1. Principe

    L'idée de base est que "tout ce qui n'est pas préfixé par le namespace "xsl:"
    (celui qui est choisi par la déclaration xmlns:xsl="http://www.w3.org/1999/XSL/Transform")
    est envoyé tel quel dans l'arbre construit. Comme on l'a dit plus haut il s'agit d'un arbre en mémoire (style DOM)
    et non pas sérialisé sous forme d'une chaîne de caractères.
    Quand nous écrivons "<html><head>...", nons sommes en train de construire un arbre par 'doc.createElement("html");'... .

  2. Espaces de noms

    1. Les espaces de noms se déclarent dans la balise-racine de la feuille de style.
      Ils y apparaissent "en vrac", sans distinguer ceux qui sont relatifs au document-source, au document-but ou à la feuille de style elle-même (à savoir "http://www.w3.org/1999/XSL/Transform" assorti de la version  - ici, '1.0').
      Plus précisément, à l'exception de ce dernier, tous les espaces de noms apparaîtront dans le document-but, même s'ils n'y jouent auscun rôle.

    2. Choix des préfixes associés aux différents espaces de noms dans un  feuille de style.
      1. Rappel : le choix du préfixe ne concerne que représentation du document comme texte dans un fichier. Le document lui-même ne connaît que les URLs qui définissent les espaces de noms. En particulier, le choix d'un espace de noms "par défaut", associé au préfixe vide, est purement affaire de commodité dans la lecture du fichier par l'œil humain : lorsque le document est traité d'un autre point de vue (notamment, comme source d'une transformation XSLT), l'espace de noms "par défaut" devient un espace de noms comme les autres...

      2. Mais comme les feuilles de style sont en général produites sous forme de fichier, il faudra bien choisir des préfixes ! À cet égard, il y a une règle importante :
        Si un de ces espaces de noms est associé au préfixe vide (namespace "par défaut"), il doit être relatif au document-but (à cause de la manière dont XPath 1.0 interprète les noms).
        En ce cas, lors de la sérialisation vers un fichier, cet espace de noms sera choisi comme espace de noms "par défaut" (et d'ailleurs, tous les namespaces apparaîtront avec les même préfixes que dans la feuille de style).

        Autrement dit, dans la feuille de style, les espace de noms du document-source doivent tous avoir des préfixes non-vides, y compris l'éventuel default namespace.

    3. Exemples
      • Toute transformation produisant du XHTML conforme doit avoir pour balise-racine :
        <xsl:stylesheet
            xmlns="http://www.w3.org/1999/xhtml"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'
        >

        (en plus de la DTD dans <xsl:output>, voyez ici).
        Et il faut que l'espace de noms-but XHTML soit par défaut (préfixe vide), sinon il est impossible de de faire fonctionner la DTD pour contrôler le résultat !
        Voyez par exemple la transformation affichant du Frantext.

      • Les transformations produisant du XSL-FO ont pour balise-racine
        <xsl:stylesheet
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     
            xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0"
        >


      • La transformation des nuances de Littré, qui prend comme source un fichier XHTML et produit du RDF.
        Sa balise-racine est :
        <xsl:stylesheet
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'
            xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
            xmlns:m2trad="http://inalco/M2Trad/"
            xmlns:h="http://www.w3.org/1999/xhtml"
        >

        avec un préfixe non-vide pour l'espace de noms-source XHTML.

  3. Les constructions de base "à la DOM"

    Il s'agit de <xsl:element>, <xsl:attribute>, <xsl:text>, <xsl:comment> (et autres).
    Comme on l'imagine, les balises <xsl:element> et <xsl:attribute> ont un attribut obligatoire "name".
    Leur emploi est tout-à-fait logique, en voici un exemple :

    <xsl:template match="person">
        <xsl:element name="name">
            <xsl:attribute name= "username">
                 <xsl:value-of select="@username"/>
            </xsl:attribute>
            <xsl:value-of select="name" />
        </xsl:element>
    </xsl:template>

    qui, appliqué à l'élément

    <person username="Jim">
        <name>Brown</name>
    </person>


    va engendrer  l'élément : <name username="Jim">Brown</name>.

    Cette rédaction parfaitement explicite est équivalente à la plus légère (mais moins lisible) :

    <xsl:template match="person">
        <name username="{@username}">
           <xsl:value-of select="name" />
        </name>
    </xsl:template>

    qui utilise l'opérateur d'évaluation d'une expression XPath dans un contexte littéral, noté par les accolades {}
    (utilisable seulement pour calculer une valeur d'attribut). [Ref]

    Remarque : ce style a l'avantage d'ête systématique, mais il est lourd à écrire et à lire.
    On peut préférer la manière plus légère utilisée dans nos premiers exemples en HTML,
    où l'arbre-résultat est écrit au lieu d'être calculé "à la DOM".
    Dans l'exemple "Jim Brown" ci-dessus, on peut faire ainsi :

    <xsl:template match="person">
      
    <name username="Jim">
         
    <xsl:value-of select="name" />
       </name>

    </xsl:template>


    mais ce qui fonctionne pour le contenu textuel, en respectant le parenthésage XML,
    ne marche pas pour calculer l'attribut !
    La syntaxe exorbitante "{@username}" a précisément pour but de contourner cette difficulté !
    Illustration au paragraphe suivant.
  4. Recopie d'un sous-arbre 

    Il arrive qu'on ait besoin de recopier intégralement un sous-arbre de l'arbre-source dans l'arbre-résultat.
    L'instruction de choix est alors <xsl:copy-of select="exp" /> à ne pas confondre avec <xsl:copy> !!!
    Elle provoque la recopie de tous les sous-arbres (y compris les attributs) sélectionnés par l'expression exp.

    L'instruction <xsl:copy> pour sa part ne recopie que la balise de l'élément courant, sans ses attributs ni ses fils.
    Le "remplissage" de l'élément ainsi recopié de manière superficielle est à la charge du programmeur !
    Cette instruction est est équivalente à : <xsl:element name="{name(.)}"/>
    Voici deux exemples simples utilisant cette instruction :
    conversion des fichiers de noms-notes du format 1 vers le format 2, et réciproquement.

    Voici un exemple où les deux instructions sont mises en œuvre.

VI. Structures de contrôle, règles paramétrées, règles nommées

  1. Pilotage des règles par <xsl:sort.../>

    Normalement, les règles lancées par <xsl:apply-templates/> s'appliquent aux éléments XML dans l'ordre où ils apparaissent
    dans le document, et par conséquent leurs produits figurent dans le même ordre en sortie,
    comme on a pu le voir abondamment dans les exemples précédents.
    Dans de nombreuses situations, typiquement dans le traitement de listes, on souhaite obtenir un résultat trié
    (par ordre alphabétique, par ordre numérique, etc).
    On utilise pour ce faire la construction <xsl:sort ... />, exclusivement à l'intérieur d'un <xsl:apply-templates>,
    ou d'un <xsl:for-each> (voir plus loin)

  2. Pilotage des règles par mode

    Avec les moyens que nous avons vus jusqu'ici, chaque élément du document-source est traité au plus une fois.
    Or il arrive que l'on veuille effectuer plusieurs traitements produisant des effets différents, au cours de la même transformation.
    Par exemple, on peut vouloir afficher un tableau de noms et notes et à la suite calculer (et afficher) la moyenne.

    XSLT nous en donne la possibilité en ajoutant à certaines règles un attribut "mode",
    qui peut également paraître dans un <xsl:apply-templates/>.
    Ceci revient à partager la base de règles en autant de sous-bases que de valeurs pour "mode",
    chaque sous-base étant appelée par un <xsl:apply-templates/> spécifique.

    En voici un exemple simple : fichier listeTriMoy.xsl

    ClasstMoy


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

    <xsl:template match="/">
      <html><head><title>Classement</title></head>
       <body>
          <p> Voici la liste des élèves par rang de mérite </p>
          <ol>
             <xsl:apply-templates />
          </ol>
          <xsl:apply-templates mode="moy" />
        </body>
      </html>
    </xsl:template>

    <xsl:template match="liste">
       <xsl:apply-templates>
        <xsl:sort select="note" data-type="number" order="descending"/>
       </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="eleve">
        <li>
            <xsl:value-of select="nom"/> :
            <xsl:value-of select="note"/>
        </li>
    </xsl:template>

    <xsl:template match="liste" mode="moy">

        <p> Voici la moyenne des notes</p>
        <b>
            <xsl:value-of select="sum(eleve/note) div count(eleve)" /> / 20
        </b>
    </xsl:template>

    </xsl:stylesheet>



  3. Structures de contrôle

    Il peut arriver que le parcours normal de l'arbre par <xsl:apply-templates/> s'avère inadéquat,
    [même si on le contrôle par un attribut select : <xsl:apply-templates select="unFiltre"/>
    et même si on gère l'ordre d'application des règles avec <xsl:sort ... /> (voir ci-dessus)]
    ou du moins malcommode à utiliser (p. ex. augmentation démesurée du nombre de règles).
    On peut alors recourir à un jeu de structures de contrôle qui comporte

    Exemple (cf. supra) : la feuille de style listHR2.xsl, qui compte trois règles, est équivalente à la suivante, qui n'en comporte qu'une :
    fichier listFE.xsl

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

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

    <xsl:template match="/">
      <html><head><title>Visualisation</title></head>
       <body>
        <h2> Voici la liste des noms et des notes</h2>
         <ol>
          <xsl:for-each select="liste/eleve">
            <li><xsl:value-of select="nom"/> :
                <xsl:value-of select="note"/>
            </li>
            <xsl:if test="position()!=last()">
               <hr />
            </xsl:if>
          </xsl:for-each>
        </ol>
      </body>
      </html>
    </xsl:template>

    </xsl:stylesheet>



    Autre exemple, avec choose, et discussion des choix de style de programmation.

  4. Appels récursifs explicites

    L'absence de variables et d'instruction d'affectation au sens usuel des langages de programmation impératifs
    limite sévèrement la programmation itérative.
    Les boucles sont indexées par la structure du fichier, non par un compteur géré par le programmeur !
    (Ce n'est pas le test qui manque, cf. ci-dessus, mais bien l'incrémentation du compteur à chaque tour de boucle.)

    Un mot sur la notion de variable en XSLT : il ne s'agit pas d'une variable au sens de C ou de Java,
    dont la valeur est modifiable par affectation au gré du programmeur,
    mais d'un nom pour une valeur, laquelle ne change pas une fois la nomination effectuée
    (comme dans les langages fonctionnels purs).
    Exemple (à suivre) :

    <xsl:variable name="indentElem" select="'   '"/> <!-- 4 espaces -->

    et dans la portée de cette déclaration la valeur de la "variable" est accessible par
    select="$indentElem".

    En revanche, XSLT offre la possibilité d'écrire des fonctions récursives, par un mécanisme de règles nommées.
    Comme les boucles sont équivalentes à des procédures récursives terminales, tout devient possible !
    Bien évidemment, les conditionnelles <xsl:if> et <xsl:choose> vont être mises à contribution
    (voir le § précédent).

  5. Règles paramétrées

    On peut également paramétrer des règles "ordinaires"(non nommées)
    qui seront alors déclenchées par

    <xsl:apply-templates select="leFiltreAdéquat">
        <xsl:with-param name="leParam" select="laValeur"/>
    </xsl:apply-templates>

    On trouvera cette technique mise en œuvre dans le pretty-print en section VI.
  6. L'exemple de la génération des histogrammes

    Toute cette mécanique permet une réalisation impeccable de la génération de l'histogramme
    dans le format demandé par le logiciel XML/SWF Charts à partir du fichier XML des noms et des notes.

    Fichier-donnée XML des noms et des notes : NomsNotes.xml
    (construit par un programme DOM à partir d'un fichier-texte) :

    <?xml version="1.0?>
    <liste >
    <eleve nom="Luc" note="12" />
    <eleve nom="Maurice" note="18" />
    <eleve nom="Juliette" note="07" />
    <eleve nom="Max" note="07" />
    <eleve nom="Pierre" note="08" />
    <eleve nom="Kevin" note="09" />
    <eleve nom="Christine" note="12" />
    <eleve nom="Joseph" note="09" />
    <eleve nom="Elisabeth" note="07" />
    <eleve nom="Elsa" note="09" />
    <eleve nom="Jules" note="11" />
    <eleve nom="Jean-Pierre" note="09" />
    <eleve nom="Franck" note="12" />
    <eleve nom="Francine" note="13" />
    <eleve nom="Aline" note="12" />
    <eleve nom="Jacques" note="09" />
    <eleve nom="Mauricette" note="12" />
    <eleve nom="Alexandre" note="09" />
    <eleve nom="Antoinette" note="12" />
    <eleve nom="Paulette" note="09" />
    <eleve nom="Ernestine" note="18" />
    <eleve nom="Julien" note="13" />
    <eleve nom="Josette" note="19" />
    </liste>

    Fichier de l'histogramme produit par la transformation : hhh.xml
    (XML sans-en-tête au format XML/SWF Charts)

    <chart>
      <chart_data>
        <row>
          <null/>
          <string>0</string>
          <string>1</string>
          <string>2</string>
          <string>3</string>
          <string>4</string>
          <string>5</string>
          <string>6</string>
          <string>7</string>
          <string>8</string>
          <string>9</string>
          <string>10</string>
          <string>11</string>
          <string>12</string>
          <string>13</string>
          <string>14</string>
          <string>15</string>
          <string>16</string>
          <string>17</string>
          <string>18</string>
          <string>19</string>
          <string>20</string>
        </row>
        <row>
          <null/>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>3</number>
          <number>1</number>
          <number>7</number>
          <number>0</number>
          <number>1</number>
          <number>6</number>
          <number>2</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>0</number>
          <number>2</number>
          <number>1</number>
          <number>0</number>
        </row>
      </chart_data>
    </chart>
    Sa visualisation par XML/SWF Charts

    Histogramme


    La feuille de style Xml2Histo.xsl : elle produit du XML.

    On note l'emploi décisif de <xsl:value-of select="count(//eleve[@note=$nbase])"/>
    pour obtenir d'un seul coup le nombre de notes égal à la valeur $nbase considérée

    <?xml version='1.0'?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
    <xsl:output  method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8" />

    <xsl:variable name="noteMax" select="'20'" />

    <xsl:strip-space elements="*" />

    <xsl:template name="Base">
        <xsl:param name="nbase"/>
        <xsl:if test="$nbase &lt;= $noteMax">
            <xsl:element name="string">
                <xsl:value-of select="$nbase"/>
            </xsl:element>
            <xsl:call-template name="Base">
                <xsl:with-param name="nbase" select="$nbase+1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>   
           
    <xsl:template name="Val">
        <xsl:param name="nbase"/>
        <xsl:if test="$nbase &lt;= $noteMax">
            <xsl:element name="number">
                <xsl:value-of select="count(//eleve[@note=$nbase])"/>
            </xsl:element>
            <xsl:call-template name="Val">
                <xsl:with-param name="nbase" select="$nbase+1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template match="/">
        <xsl:element name="chart">
            <xsl:element name="chart_data">
                <xsl:element name="row">
                    <xsl:element name="null"/>
                    <xsl:call-template name="Base">
                        <xsl:with-param name="nbase" select="0"/>
                    </xsl:call-template>
                </xsl:element>
                <xsl:element name="row">
                    <xsl:element name="null"/>
                    <xsl:call-template name="Val">
                        <xsl:with-param name="nbase" select="0"/>
                    </xsl:call-template>
                </xsl:element>
            </xsl:element>
        </xsl:element>
    </xsl:template>

    </xsl:stylesheet>




VII. Un exemple (relativement) complet

  1. But

    Il s'agit de réaliser avec XSLT le pretty-print de petits programmes comme qr.xml que nous avons vu plus haut.
    On souhaite pouvoir le faire en mode texte (ici, sous Unix en ligne de commande ):

    jfp% xsltproc qr.xml
    VAR a; b; q; r; LIRE a ;
    LIRE b ;
    q := 0 ;
    r := a ;
    TANTQUE t &gt;= b FAIRE
    q := q + 1 ;
    r := r - b
    FINTQ ;
    ECRIRE q ;
    ECRIRE r
    .
    jfp%


    On souhaite en outre pouvoir faire apparaître le programme dans une page Web, comme de juste.

    qr

    Voyez un exemple plus complet, avec un programme un peu plus compliqué : qrd.xml.
  2. Principe

    L'opération se fait en composant 3 feuilles de style.
    En effet, le travail principal consiste à effectuer le pretty-print en mode texte.
    Dans cette tâche, on distingue le travail particulièrement délicat de l'impression des expressions
    arithmétiques et booléennes avec un parenthésage minimum, compte-tenu des règles traditionnelles
    de précédence entre opérateurs [on écrit "2 * x + 1" et non pas "((2 * x) + 1)"].
    L'impression des expressions est donc effectuée par une feuille de style indépendante
    ExpAr/ExpArBoolMIL2ap.xsl, qui est importée par la feuille principale MIL/PrettyMIL.xsl.
    L'insertion du texte ainsi produit dans une page Web (grâce à la balise HTML <pre>)
    est effectuée par une troisième feuille de style HtmlMIL.xsl, qui importe à son tour PrettyMIL.xsl.

  3. La feuille de style finale HtmlMIL.xsl

    Cette feuille de style se borne à mettre en scène le produit de PrettyMIL.xsl dans un "cadre HTML".
    Par conséquent elle proclame <xsl:output method="html" ... />,
    au contraire des deux autres qui sont neutres vis-à-vis de la méthode de sortie, pour pouvoir être employées
    dans toutes les situations.


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

    <xsl:output method="html"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8"
    indent="yes"/>

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

    <xsl:template match="/">
    <html>
    <head>
    <title>Programme MIL</title>
    </head>
    <body>
    <pre>
    <xsl:apply-templates/>
    </pre>
    </body>
    </html>
    </xsl:template>

    </xsl:stylesheet>

  4. La feuille de style principale PrettyMIL.xsl

    C'est elle qui fait le gros du travail : le traitement des instructions.
    Les expressions forment un sous-langage à part, où les questions d'indentation n'entrent pas.
    La syntaxe à précédences dans les expressions arithmétiques offre suffisamment de difficultés
    pour qu'on sépare le traitement des expressions de celui des instructions,
    en le confiant à une feuille de style séparée.
    On peut ainsi mettre au point les deux feuilles séparément, par exemple en faisant fonctionner PrettyMIL
    avec un imprimeur d'expressions en notation complètement parenthésée, beaucoup plus facile à réaliser.

    <?xml version='1.0'?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
    <xsl:output omit-xml-declaration="yes"/> <!-- non committal -->

    <!-- Pretty MIL (révision INaLCO-M2-IM octobre 2008)
         avec indentations et importation des ExpAr -->
        
    <xsl:strip-space elements="*" />

    <xsl:include href="../ExpAr/ExpArBoolMIL2ap.xsl"/>

    <xsl:variable name="indentElem" select="'   '"/>

    <xsl:template name="indent">
    <!-- en l'absence d'effets de bord, la boucle habituelle se réalise
         avec une récursion terminale -->
        <xsl:param name="prof"/>
        <xsl:if test="$prof &gt; 0">
            <xsl:value-of select="$indentElem"/>
            <xsl:call-template name="indent">
                <xsl:with-param name="prof" select="$prof - 1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template match="/Prog">
       <xsl:apply-templates select="Variables"/>  <!-- marque explicitement l'ordre -->
       <xsl:apply-templates select="Sequence">
          <xsl:with-param name="prof" select="0"/>
       </xsl:apply-templates>
       <xsl:text>.</xsl:text> <!-- le point final -->
    </xsl:template>

    <xsl:template match="Variables">
       <xsl:text>VAR&#32;</xsl:text> <!-- ASCII-32 = espace -->
       <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="Variables/Var[position()!=last()]">
        <xsl:value-of select="@nom"/>
        <xsl:text>; </xsl:text>
    </xsl:template>

    <xsl:template match="Variables/Var[position()=last()]">
        <xsl:value-of select="@nom"/>
        <xsl:text>.&#10;</xsl:text>
    </xsl:template >

    <xsl:template match="Sequence">
        <xsl:param name="prof"/>
        <!-- l'emploi d'une itération évite de dédoubler toute la collection
             des règles relatives aux différentes instructions
        -->
        <xsl:for-each select="*"> <!-- tous les enfants, dans l'ordre -->
            <xsl:call-template name="indent">
                <xsl:with-param name="prof" select="$prof"/>
           </xsl:call-template>
           <xsl:apply-templates select="."> <!-- chaque enfant -->
                <xsl:with-param name="prof" select="$prof"/>
            </xsl:apply-templates>     
           <xsl:if test="position()&lt;last()"> <!-- l'entité est obligatoire en XSLT -->
               <xsl:text> ;&#10;</xsl:text> <!-- ASCII-10 = LF -->
           </xsl:if>
           <xsl:if test="position()=last()">
               <xsl:text>&#10;</xsl:text>
           </xsl:if>      
         </xsl:for-each>
    </xsl:template>

    <xsl:template match="Espace">
        <xsl:text>ESP</xsl:text>
     </xsl:template>
     
     <xsl:template match="Ligne">
        <xsl:text>LIG</xsl:text>
     </xsl:template>
     
    <xsl:template match="Lecture">
        <xsl:text>LIRE&#32;</xsl:text>
        <xsl:value-of select="Var/@nom"/>
     </xsl:template>

    <xsl:template match="Ecriture">
        <xsl:text>ECRIRE&#32;</xsl:text>
        <xsl:apply-templates select="*"/>
     </xsl:template>

    <xsl:template match="Affectation">
        <xsl:value-of select="Var/@nom"/>
        <xsl:text>&#32;:=&#32;</xsl:text>
        <xsl:apply-templates select="*"/>    
     </xsl:template>
     
     <xsl:template match="Boucle">
        <xsl:param name="prof"/>
        
        <xsl:text>TANTQUE&#32;</xsl:text>
        <xsl:apply-templates select="Comparaison"/>
        <xsl:text>&#32;FAIRE&#10;</xsl:text>
        <xsl:apply-templates select="Sequence">
           <xsl:with-param name="prof" select="$prof + 1"/>
        </xsl:apply-templates>
       
        <xsl:call-template name="indent">
            <xsl:with-param name="prof" select="$prof"/>
        </xsl:call-template>
        <xsl:text>FINTQ</xsl:text>
     </xsl:template>
     
     <xsl:template match="Conditionnelle">
        <xsl:param name="prof"/>

        <xsl:text>SI&#32;</xsl:text>
        <xsl:apply-templates select="Comparaison"/>
        <xsl:text>&#32;ALORS&#10;</xsl:text>
        <!-- voir Bin : il ya deux fils "Sequence" à distinguer ! -->
        <xsl:apply-templates select="./*[position()=2]">
           <xsl:with-param name="prof" select="$prof + 1"/>
        </xsl:apply-templates>
       
        <xsl:call-template name="indent">
           <xsl:with-param name="prof" select="$prof"/>
        </xsl:call-template>
        <xsl:text>SINON&#10;</xsl:text>
        <xsl:apply-templates select="./*[position()=3]">
           <xsl:with-param name="prof" select="$prof + 1"/>
        </xsl:apply-templates>

        <xsl:call-template name="indent">
           <xsl:with-param name="prof" select="$prof"/>
        </xsl:call-template>
        <xsl:text>FINSI</xsl:text>
     </xsl:template>
     
    <!-- ======== Expressions arithmétiques & booléennes  : importées ======== -->

    <!-- et voila tout ! -->
    </xsl:stylesheet>



  5. La feuille spéciale pour les expressions ExpArBoolMIL2ap.xsl

    Pour illustrer le propos relatif à la division entre instructions et expressions, voici d'abord
    une feuille de style réalisant l'impression des expressions en syntaxe complètement parenthésée,
    utile pour faire des essais. (ExpArBoolMIL2cp.xsl)

    <?xml version='1.0'?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
    <xsl:output omit-xml-declaration="yes"/> <!-- non committal -->

    <xsl:strip-space elements="*" />

    <!-- ======== Expressions arithmétiques & booléennes (version 2007) ========

         munies d'une racine postiche "ExpAr" pour essais.
         Nécessité de préciser quels éléments-fils sont visés par apply-templates
         sous peine de voir répercuter les blancs précédants,
         à cause des règles par défaut !
         De même, <xsl:text>(</xsl:text> vaut mieux que '(' sec,
         car le saut de ligne suivant vient avec !

     -->
        
    <xsl:template match="/ExpAr">
    <!--Marqueur sans valeur sémantique, appel récursif global -->
         <xsl:apply-templates/>
    </xsl:template>

    <!-- Traitement différencié des trois sortes d'expressions -->

    <xsl:template match="Bin">  <!-- Le cas intéressant ! -->
       <!-- on écrit d'abord la parenthèse fermante -->
       <xsl:text>(</xsl:text>
       <!-- On ne peut pas demander simplement "Bin|Cte|VarExp", car
       alors les DEUX opérandes seraient traités simultanément !!!
       Il faut absolument les distinguer par position -->
       <!-- (1) appel récursif global sur l'opérande gauche -->
       <xsl:apply-templates select="./*[1]"/>
       <!-- (2) on écrit l'opérateur, entre deux blancs -->
       <xsl:value-of select="concat('&#32;', @op, '&#32;')"/>
       <!-- (3) appel récursif global sur l'opérande droit -->
       <xsl:apply-templates select="./*[2]"/>
       <!-- enfin on écrit la parenthèse fermante -->
        <xsl:text>)</xsl:text>
    </xsl:template>

    <xsl:template match="Cte">
       <xsl:value-of select="@val"/>
     </xsl:template>

    <xsl:template match="VarExp">
         <xsl:value-of select="Var/@nom"/>
    </xsl:template>

    <xsl:template match="Comparaison">
    <!-- sur le même modèle que Bin -->
       <xsl:apply-templates select="./*[1]"/>   
       <xsl:value-of select="concat('&#32;', @op, '&#32;')"/>
       <xsl:apply-templates select="./*[2]"/>
     </xsl:template>
     
    <!-- et voila tout ! -->
    </xsl:stylesheet>


    Et voici la version officielle, avec le parenthésage minimal
    compte-tenu des précédences entre opérateurs (ExpArBoolMIL2ap.xsl).

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

    <!-- ======== Expressions arithmétiques & booléennes (version 2007) ========
        Une réalisation de l'impression en syntaxe à précédences (2 niveaux)
     -->
        
    <xsl:strip-space elements="*" />

    <xsl:template match="/ExpAr">
    <!--Marqueur sans valeur sémantique, appel récursif global -->
         <xsl:apply-templates/>
    </xsl:template>

    <!-- L'impossibilité de définir une fonction de précédence
         conduit à traiter séparément les 2 classes d'opérateurs -->

    <xsl:template match="Bin[@op = '+' or @op = '-']">
        <!-- on ne parenthèse pas l'opérande gauche
                          (association à gauche ou précédence) -->
          <xsl:apply-templates select="./*[1]"/>
          <!-- on écrit l'opérateur -->
        <xsl:value-of select="concat('&#32;', @op, '&#32;')"/>
        <!-- on parenthèse l'op. droit sauf si précédence -->
        <xsl:choose>
              <xsl:when test="./*[position()=2 and (@op='+' or @op='-')]">
                  <xsl:text>(</xsl:text>
                  <xsl:apply-templates select="./*[2]"/>
                  <xsl:text>)</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                   <xsl:apply-templates select="./*[2]"/>     
              </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="Bin[@op = '*' or @op = '/']">
        <!-- on parenthèse l'opérande gauche si précédence) -->
        <xsl:choose>
              <xsl:when test="./*[position()=1 and (@op='+' or @op='-')]">
                  <xsl:text>(</xsl:text>
                  <xsl:apply-templates select="./*[1]"/>
                  <xsl:text>)</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                   <xsl:apply-templates select="./*[1]"/>     
              </xsl:otherwise>
        </xsl:choose>
        <!-- on écrit l'opérateur -->
        <xsl:value-of select="concat('&#32;', @op, '&#32;')"/>
        <!-- on parenthèse l'op. droit dans tous les cas Bin -->
        <xsl:choose>
              <xsl:when test="./*[position()=2 and name()='Bin']">
                  <xsl:text>(</xsl:text>
                  <xsl:apply-templates select="./*[2]"/>
                  <xsl:text>)</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                   <xsl:apply-templates select="./*[2]"/>     
              </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="Cte">
       <xsl:value-of select="@val"/>
     </xsl:template>

    <xsl:template match="VarExp">
         <xsl:value-of select="Var/@nom"/>
    </xsl:template>

    <xsl:template match="Comparaison">
    <!-- sur le même modèle que Bin -->
       <xsl:apply-templates select="./*[position()=1]"/>   
       <xsl:value-of select="concat('&#32;', @op, '&#32;')"/>
       <xsl:apply-templates select="./*[position()=2]"/>
     </xsl:template>
     
    <!-- et voila tout ! -->
    </xsl:stylesheet>