IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

XSL/XSLT/XPATH XML Discussion :

[XSLT] Réutiliser le code des variables


Sujet :

XSL/XSLT/XPATH XML

  1. #1
    Membre averti Avatar de titouille
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    353
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2005
    Messages : 353
    Points : 356
    Points
    356
    Par défaut [XSLT] Réutiliser le code des variables
    Bonjour à tous !!

    Je me pose des questions quant à la faisabilité de la chose, mais voilà le problème...
    Je parse des fichiers xsl-fo. Ces derniers contiennent bcp d'attributs de mise en forme dans les balises, genre font-size, font-family, padding-left, padding-top, etc...

    J'ai plusieurs types de balises : fo:block, fo:block-container, fo:inline, fo:table, fo:table-row, fo:table-cell (et bien d'autres) mais celles que je viens d'énumérer sont les plus susceptibles de contenir ces attributs de formatage.

    Dans mon document xsl actuel, j'ai des templates pour chacune de ces balises, et chaque template prend en charge un certain nombre de tests pour récupérer ces variables de formatage, exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    <xsl:template match="fo:inline">
    	<xsl:variable name="size">
    		<xsl:if test="./@font-size != ''">font-size:<xsl:value-of select="number( substring-before( ./@font-size, 'px' ) ) + $incrementFont" />px;</xsl:if>
    	</xsl:variable>
    	<xsl:variable name="family">
    		<xsl:if test="./@font-family != ''">font-family:<xsl:value-of select="./@font-family" />;</xsl:if>
    	</xsl:variable>
    	<xsl:variable name="weight">
    		<xsl:if test="./@font-weight != ''">font-weight:<xsl:value-of select="./@font-weight" />;</xsl:if>
    	</xsl:variable>
    	<span style="{$size}{$family}{$weight}">
    		<xsl:apply-templates />
    	</span>	
    </xsl:template>
     
     
    <xsl:template match="fo:table-row">
    	<xsl:variable name="weight"><xsl:if test="./@font-weight != ''">font-weight:<xsl:value-of select="./@font-weight" />;</xsl:if></xsl:variable>
    	<xsl:variable name="size">
    		<xsl:if test="./@font-size != ''">font-size:<xsl:value-of select="number( substring-before( ./@font-size, 'px' ) ) + $incrementFont" />px;</xsl:if>
    	</xsl:variable>
    	<xsl:variable name="family"><xsl:if test="./@font-family != ''">font-family:<xsl:value-of select="./@font-family" />;</xsl:if></xsl:variable>
    	<tr style="{$weight}{$size}{$family}">
    		<xsl:apply-templates />
    	</tr>
    </xsl:template>
    Vous pourrez remarquer qu'il y a une redondance sur les variables des 2 templates : font-family, font-size et font-weight sont dans les 2 templates, et possède un code identique...

    La question que je me pose, c'est : est-ce qu'il est possible d'éviter ces redondances... J'imagine que oui, et je penche pour 2 hypothèses, mais je ne vois pas très bien comment les mettre en place (par manque de connaissance en XSL).

    La première, ça serait de pouvoir déclarer le code de la variable en dehors de tout template, et d'y faire appel dans les templates nécessitant le test... Mais ça j'y crois assez peu, d'après ce que j'ai pu comprendre...

    La seconde, ça serait de créer un "gros" template qui teste toute les variables de formatage possible et imaginable, et que j'appelle avec un "call-template" en lui passant le type de balise testé...
    Exemple :

    - call-template with-param type=fo:block,
    - call-template with-param type=fo-block-container,
    - call-template with-param type=fo:table

    Dans mon template, je teste d'abord toutes les variables, puis ensuite, selon le paramètre "type", je crée un div (fo:block, fo:block-container), un span (fo:inline), une table (fo:table), une ligne (fo:table-row), etc...

    J'imagine que c'est la solution la plus envisageable, mais ce que je n'arrive pas à comprendre, c'est comment faire...

    A la base, je fais appel à

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <xsl:template match="fo:root">
    <xsl:apply-templates />
    </xsl:template>
    ce qui va permettre de parcourir récursivement toutes les balises sans se préoccuper de leur "type". Maintenant je me demande comment faire pour que :

    - si pour une balise donnée un template spécifique existe (exemple : fo:basic-link nécessite un traitement particulier) il utilise le template spécifique
    - si pour une balise donnée il n'existe pas de template spécifique il utilise le template spécial qui prend en charge toutes les variables nécessaire et selon le paramètre "type" il va me sortir un output particulier (table, div, span, etc...)

    Je suis à l'écoute de vos suggestions, d'avance merci :-)

  2. #2
    Membre averti Avatar de titouille
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    353
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2005
    Messages : 353
    Points : 356
    Points
    356
    Par défaut
    Bon ben en fait je pense avoir trouvé un début de solution en utilisant

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <xsl:template match="fo:root">
      <xsl:apply-templates>
        <xsl:with-param name="type">test</xsl:with-param>
      </xsl:apply-templates>
    </xsl:template>
     
    <xsl:template match="*">
      <xsl:param name="type"></xsl:param>
      ...
      <xsl:apply-templates>
        <xsl:with-param name="type">test</xsl:with-param>
      </xsl:apply-templates>
    </xsl:template>
    Et dans chacun de mes templates faisant un appel récursif avec apply-templates, à chaque fois il faut passer le paramètre correspondant au type pour qu'il soit envoyé plus loin dans la descendance.

    Mon problème maintenant, c'est d'obtenir le nom de la balise (fo:block, fo:block-container, etc...) que je dois passer en paramètre. Mais à priori, c'est faisable

  3. #3
    Membre averti Avatar de titouille
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    353
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2005
    Messages : 353
    Points : 356
    Points
    356
    Par défaut
    Ok, plus simple encore...

    En utilisant <xsl:param name="type" select="name()" /> directement dans mon template générique, j'obtiens le type de balise et je peux traiter selon ce type.

  4. #4
    Membre expérimenté
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    1 466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 466
    Points : 1 610
    Points
    1 610
    Par défaut
    Tu peux factoriser du code de plusieurs templates identiques comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <xsl:template match="fo:block | fo:table">
      ...
    </xsl:template>
    Et si tu places des templates spécifiques avant un générique (avec match="*"), ils devraient avoir la priorité.
    Tu peux aussi regarder du codé de l'attribut "mode" des templates pour ajouter du conditionnement au moment de "l'apply-template".

  5. #5
    Membre averti Avatar de titouille
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    353
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2005
    Messages : 353
    Points : 356
    Points
    356
    Par défaut
    Salut Morbo !

    Ok pour la factorisation, je ne savais pas. Mais comme chaque template utilise des attributs de style communs ainsi que des différents, j'ai opté pour faire un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <template name="xxx">
    ...
    </template>
    qui est appelé à partir de mon template générique en passant les paramètres nécessaire.
    Mon template générique, quant à lui, déclare les variables communes au début, puis un xsl:choose permet de choisir le bon template à appliquer selon le type de noeud, tout en déclarant des variables supplémentaires si nécessaire avant de faire un call-template sur le template adéquat.

    Je n'ai par contre pas vu de "priorité". Mon template générique est en début de script (ou peut-être en fin de script, puisque les autres templates sont dans d'autres fichiers et pour chaque fichier un include est fait en début de mon script principal). Mais j'ai d'autres templates spécifiques (exemple, le fo:marker sur lequel tu m'as aidé dans un autre thread) qui sont situés en dessous du générique, et tout passe correctement.

    J'avais déjà fait un ou deux tests avec l'attribut "mode" mais c'est pas très clair pour moi... Exemple :

    Une fo:table est déclarée avec des éléments tels que fo:table-column (qui déclare une colonne dans la table, donc plusieurs fo:table-column si plusieurs colonnes) puis un fo:table-body qui va contenir lui-même un ou plusieurs fo:table-row (qui contiendra un ou plusieurs fo:table-cell)

    J'ai fait un template "table-head" pour fo:table-column puis un autre ("table-body") pour fo:table-body.

    le template table-head est sensé créer pour chaque colonne une "en-tête" dans un tableau html standard (TH) mais si j'appelle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    <table cellspacing="0" cellpadding="0" style="border-collapse:collapse;{$align}{$valign}{$size}{$family}{$margintop}{$marginbottom}{$width}{$border}{$borderstyle}">
    	<thead>
    		<tr><xsl:apply-templates mode="table-head" /></tr>
    	</thead>
    	<tbody>
    		<xsl:apply-templates mode="table-body" />
    	</tbody>
    </table>
    Il crée les colonnes, mais il va également m'afficher tout ce qui est en dessous (le contenu de fo:table-body) non formatté, puis va relancer le traitement pour table-body (2ème apply-templates).

    Je suis obligé de rajouter un select dans le apply-template pour qu'il ne prenne que les noeuds fo:table-column :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <thead>
    	<tr><xsl:apply-templates mode="table-head" select="./fo:table-column" /></tr>
    </thead>
    Pourtant, mon template "table-head" est spécifique pour les fo:table-column :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <xsl:template match="fo:table-column" mode="table-head">
    	<xsl:variable name="width">
    		<xsl:if test="./@column-width != ''">width:<xsl:value-of select="./@column-width" />;</xsl:if>
    	</xsl:variable>
    	<th style="{$width}"></th>
    </xsl:template>
    Y aurait-il une précédence sur le mode par rapport à la comparaison (match) ? Je n'ai pas trop compris ce point-là, pourquoi dois-je mettre un select dans mon apply-templates alors que mon template est sensé ne prendre en compte que les noeuds correspondants à la comparaison, en l'occurence fo:table-column.

    Mais bref. En gros mon taff est ok, j'ai pu faire ce que je voulais tout en optimisant même un peu mon code pour avoir moins de redondances dans mes templates... Après je ne sais pas si c'est optimisé au niveau du temps de parsing, je pense que non vu que mes tests de variables se font pour chaque type de noeud alors que tous les types de noeuds n'ont pas forcément toujours les mêmes variables communes, mais au moins c'est pas trop le fouilli dans mon code.

  6. #6
    Membre expérimenté
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    1 466
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 466
    Points : 1 610
    Points
    1 610
    Par défaut
    Woaw, que de questions .
    Je vais répondre à ce que j'ai retenu.
    Pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    <table cellspacing="0" cellpadding="0" style="border-collapse:collapse;{$align}{$valign}{$size}{$family}{$margintop}{$marginbottom}{$width}{$border}{$borderstyle}">
    	<thead>
    		<tr><xsl:apply-templates mode="table-head" /></tr>
    	</thead>
    	<tbody>
    		<xsl:apply-templates mode="table-body" />
    	</tbody>
    </table>
    Quand tu fais un apply-templates sans "select" il parcourt tous les attributs et noeuds fils et déclenche les templates qui match ces éléments. Le "mode" n'est utilisé que s'il y a ambiguité sur 2 templates à éxécuter, sinon celui éxistant est utilisé (mode ou pas). C'est pour ça que tu va afficher plusieurs fois la même chose ici.
    J'utilise presque systématiquement le "select" dans un apply-templates pour contrôler les éléments que je veux déclencher.
    Je fais mes conditions dans les "select" plutôt qu'à l'intérieur des templates avec des "if" ou "when".

    Après les template "nommé", je les utilise que lorsque je souhaite un traitement dé-corrélé d'un noeud.

  7. #7
    Membre averti Avatar de titouille
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    353
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2005
    Messages : 353
    Points : 356
    Points
    356
    Par défaut
    Re,

    Rapport a tes explications qui me semble bien claires, je me demandais alors si mes templates nommés pour fo:table-column et fo:table-body nécessitaient réellement d'avoir un "match" sur le bon noeud, puisque je cible déjà dans xsl:apply-templates avec le select et le mode, mais le matching est bien nécessaire. Si je le vire, plus rien ne va. Ce qui me semble un peu étonnant puisque mon apply-templates donne explicitement la marche à suivre : utiliser le template {mode} sur les noeuds {select}.

    Je ne maitrise pas assez pour bien comprendre toutes les subtilités, mais bon, j'ai une excuse, ça fait seulement depuis deux jours que je m'y suis mis

    Je fais mes conditions dans les "select" plutôt qu'à l'intérieur des templates avec des "if" ou "when".
    Je pense que ça dépend de ce qu'on veut faire. Je n'ai pas assez de recul pour imaginer des solutions faciles. Je monte mon truc brique par brique et dès fois je me rend compte que je peux simplifier de ci de là, mais j'imagine que ce n'est qu'avec l'expérience qu'on arrive à cibler les bonnes solutions du premier coup selon les besoins.

    En tout cas merci pour tes explications, ça rend déjà certaines choses plus claires

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 09/08/2006, 16h14
  2. [XSLT] Problème avec la portée des variables
    Par sheura dans le forum XSL/XSLT/XPATH
    Réponses: 17
    Dernier message: 10/01/2006, 13h49
  3. [XSLT] Réutiliser un contexte mémorisé dans une variable
    Par camboui dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 02/01/2006, 13h49
  4. Réponses: 10
    Dernier message: 06/10/2005, 23h25
  5. [C#][XSLT] Passer des variables
    Par Landolsi dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 30/09/2005, 15h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo