Révisé le 05 Juin 2018Generate XSL - GitHub
Generate XSL
Introduction
Generate-XSL est un langage d'annotions permettant de décrire la compilation d'une transformation XSL, en pré-calculant certaines évaluations dans un but d'optimisation des performances.
Ces annotations s'expriment sous forme d'éléments et d'attributs XML appartenant
au
namespace Generate-XSLhttp://jimetevenard.com/ns/generate-xsl
ajoutés dans la
transformation XSL.
Après compilation, on obtient une XSLT générée dans laquelle la valeur des variables, le résultat des appels de fonction, des appels de templates ou des structures de boucles et conditions annoté(e)s sont sérialisés.
L'objectif principal de ce système d'annotation est d'optimiser le traitement d'une
XSLT
sans affecter son fonctionnement d'origine.
Ainsi cette XSLT reste maintenable et
testable, et des test comparés de la XSL
source et de la XSL
générée permettent d'identifier facilement les éventuels effets de
bord de la compilation.
Exemple :
Le code suivant :
<xsl:choose generate:build="#all" > <xsl:when test="true()" > <xsl:value-of select="'Yep !'" /> </xsl:when> <xsl:otherwhise> <xsl:value-of select="'Nope !'" /> </xsl:otherwhise> </xsl:choose>
Devient, après compilation :
<xsl:value-of select="'Yep !'" />
Dans cet exemple, on a exprimé par l'attribut @generate:build
la
volonté de pré-évaluer ce <xsl:choose/>
.
Ainsi, les différents
xsl:when/@test
ont été évalués et seul le code contenu dans le cas
pertinent est retranscrit dans la XSL
générée
Modes de pré-évaluation
Comme en XSLT, la notion de modes existe dans la compilation XSLT.
En effet, considérons le cas suivant :
<xsl:template match="/" > <!-- appel de template compilé --> <xsl:call-template generate:build="#all" generate:use-build-mode="call-foo" name="foo" /> </xsl:template> <xsl:template name="foo" > <xsl:sequence generate:build="call-foo" select="someXPath" /> </xsl:template>
Dans le présent exemple, il est souhaité que l'élément <xsl:sequence/>
du template
foo ne soit pré-évalué que dans le cadre d'un appel lui-même pré-évalué de ce template.
Il se pourrait en effet que ce template soit appelé ailleurs dans la XSLT, indépendamment du processus de pré-évaluation.
Ici, l'élement <xsl:sequence/>
ne sera pré-évalué que dans le mode call-foo
,
mode que nous avons spécifié via l'attribut @generate:use-build-mode
de <xsl:call-template/>
#all représente tous les modes de compilation.
Contexte de la pré-évaluation
La pré-évaluation de la XSL est une phase liée au build d'un projet.
Elle est donc totalement ignorante des données ou du contexte d'exécution.
L'ensemble du contexte XPath, variables, paramètres, etc. disponibles pendant
la phase de compilation
est appelée contexte de génération
Le développeur qui met en place les annotations doit s'assurer que les expressions
XPath évaluées sont effectivement évaluables dans ce contexte.
Il peut s'aider en cela des éléments <generate:use-import/>
,
<generate:use-variable/>
et <generate:use-param/>
,
ainsi que de l'attribut @generate:use
pour rendre visibles des variables, des paramètres ou des
imports (et donc les variables globales, les paramètres et les fonctions qu'ils déclarent)
dans le contexte de génération.
Implémentation XSLT
Le présent projet est une implémentation en XSLT de ce système d'annotations.
La transformation generate.xsl
prend en entrée la transformation XSL source,
et produit en sortie une XSL intermédiaire.
Cette XSL intermédiaire doit ensuite être appelée sans entrée (template inital "init"),
et produit en sortie la XSL compilée.
Si des paramètres ont été annotés comme visibles dans le contexte de génération, ils
peuvent être passés
à cette XSL intermédiaire.
Remarque :
En l'absence d'annotations, la XSL générée est une copie de la XSL source.
Driver Java
Une API Java basé sur Saxon est fourni pour faciliter la transformation.
La classe com.jimetevenard.xslt.Driver
assure la compilation d'après un fichier de scenari.
Fichier de scenari
Chaque scénario décrit la compilation d'une XSL :
- Le fichier source
- L'emplacement cible pour la XSL compilée
- Les paramètres à utiliser
<scenari> <scenario name="mon-scenario" > <source-xsl-path>source.xsl</source-xsl-path> <target-xsl-path>target-dir/generated.xsl</target-xsl-path> <params> <param name="param-Qname" select="//xpath" /> <!-- <param... />* --> </params> </scenario> <!-- <scenario... />* --> </scenari>Schéma XSD du fichier de scenari
Plugin Maven
Le plugin Maven Maven-XSL-Generator utilise cette API, et permet de lancer la compilation Generate-XSL lors du build de votre projet.
Syntaxe du langage d'annotations
Les annotations consistent en éléments <generate:*/>
et attributs @generate:*
.
Les attributs @generate:*
se placent sur des instructions <xsl:*/>
,
pour spécifier le comportement, lors de la génération, des instructions sur lesquelles
ils sont placés.
Les éléments <generate:*/>
doivent être des enfants de la racine <xsl:stylesheet/>
ou d'autres éléments <generate:*/>
.
Ceci pour ne pas perturber le comportement de la XSLT.
Attributs et éléments du langage
-
Compiler des instructions XSL
- Compiler une instruction par l’ajout de l’attribut
@generate:build
- Dupliquer un template avec l’élément
<generate:copy-template/>
-
Modifier le templace dupliqué avec les éléments
<generate:set-match/>
,<generate:set-mode/>
,<generate:set-priority/>
,<generate:set-name/>
,<generate:set-as/>
,<generate:set-visibility/>
,<generate:redefine-variable/>
et<generate:with-param/>
-
Modifier le templace dupliqué avec les éléments
- Compiler une instruction par l’ajout de l’attribut
-
Contrôle des modes de compilation
- Spécifier un mode de compilation avec l'attribut
@generate:use-build-mode
- La valeur de l'attribut
@generate:build
correspond au(x) mode(s) de compilation cible(s)
- Spécifier un mode de compilation avec l'attribut
-
Modifier la structure de la XSLT
- Générer des variables (globales?(copy-template?)) avec l’élément
<generate:variable/>
- Redéfinir une variable existante avec
<generate:redefine-variable/>
- Générer des imports avec l’élément
<generate:import/>
- Générer des paramètres globaux avec l’élément
<generate:param/>
- Ajouter un bloc de code avec l’élément
<generate:target-block/>
- Générer des variables (globales?(copy-template?)) avec l’élément
-
Structures de contrôle du langage d’annotations
- Structures conditionnelles : éléments
<generate:if/>
et<generate:choose/>
- La boucle
<generate:for-each/>
- Structures conditionnelles : éléments
-
Utiliser des variables et paramètres dans le contexte de génération
- l’attribut
@generate:use
- les éléments
<generate:use-variable/>
,<generate:use-import/>
et<generate:use-param/>
- l’attribut
-
Identifier une instruction XSL
- Ajout d’un identifiant par l’attribut
@generate:id
- Ajout d’un identifiant par l’attribut
Compiler des instructions XSLT
Attribute generate:build
Cet attribut sert à indiquer que l'on souhaite pré-évaluer l'instruction.
Valeurs :
Sur les éléments <xsl:variable/>
Le résultat généré est alors une copie de l'instruction <xsl:variable/>
dont le contenu est un sequence constructor
équivalent à la sérialisation
du resultat de l'évaluation de l'attribut @select
ou du contenu initial de
l'élément <xsl:variable/>
<xsl:variable name="foo" generate:build="#all" select="concat('he','llo')" />
Devient, après compilation :
<xsl:variable name="foo" >hello</xsl:variable>
Sur les éléments <xsl:value-of/>
TODO vérifier l'implémentation...
Sur les éléments <xsl:call-template/>
Nous avons la possibilité de pré-évaluer un appel de template
nommé de type <xsl:call-template name="foo" />
Le résultat dans la xsl-générée est la sérialisation de la séquence de retour du template.
Peut être complété par un attribut @generate:use-build-mode
.
Les paramètres spécifiés s'appliquent
TODO exemple
Sur les éléments <xsl:with-param/>
Comportement identique au cas des <xsl:variable/>
décrit ci-dessus
Sur les éléments <xsl:if/>
et <xsl:choose/>
>
Le(s) attributs @test
correspondant alors sont évalués.
Exemple, le code suivant
<xsl:if generate:build="#all" test="true()" > <xsl:message>Hello !</xsl:message> </xsl:if>
Devient, après compilation :
<xsl:message>Hello !</xsl:message>
Sur les éléments <xsl:for-each/>
Le contenu de bloc est reproduit et décliné pour chaque élément de la séquence
spécifiée en @select
<xsl:for-each generate:build="#all" select="('foo','bar','baz')" > <xsl:message generate:build="#all" select="current()" /> </xsl:for-each>
Devient, après compilation :
<xsl:message>foo</xsl:message> <xsl:message>bar</xsl:message> <xsl:message>baz</xsl:message>
Element generate:copy-template
<xsl:template/>
de la XSL
source identifié soit par :
@generate:id
exprimée sous forme d'un
attribut @idRef
@name
exprimée via un attribut
@name
sous réserve que celui-ci soit présent et
unique.
Attributs et valeurs :
@idRef
: Identifiant (@generate:id
) du template à cibler
@name
: Nom du template à cibler
<generate:set-match/>
<generate:set-mode/>
<generate:set-priority/>
<generate:set-name/>
<generate:set-as/>
<generate:set-visibility/>
qui permettent de redéfinir les
attributs correspondants du template.
Exemple, le code suivant :
<generate:for-each select="('a','p','div')" name="currentElement" > <generate:copy-template idRef="bar" > <generate:set-match value="{$currentElement}" /> </generate:copy-template> </generate:for-each> <xsl:template match="*" generate:id="bar" > <xsl:message select="name()" /> </xsl:template>
Devient, après compilation :
<xsl:template match="a" > <xsl:message select="name()" /> </xsl:template> <xsl:template match="p" > <xsl:message select="name()" /> </xsl:template> <xsl:template match="div" > <xsl:message select="name()" /> </xsl:template>
- Element
generate:set-match
- Element
generate:set-mode
- Element
generate:set-priority
- Element
generate:set-name
- Element
generate:set-as
- Element
generate:set-visibility
Ces éléments permettent, dans un <generate:copy-template/>
de modifier
l'attribut correspondant du template.
Element generate:with-param
TODO !
Modifier la structure de la XSLT
Element generate:variable
Crée une <xsl:variable/>
dans la XSL
générée
L'évaluation ou non de la variable durant la compilation peut être exprimée par
l'attribut @generate:build
Exemple, le code suivant
<generate:variable name="foo" select="count('foo','bar')" /> <generate:variable name="bar" generate:build="#all" select="count('hello','world')" />
Devient, après compilation :
<xsl:variable name="foo" select="count('foo','bar')" /> <xsl:variable name="bar" >2</xsl:variable>
Element generate:redefine-variable
@id
, traité comme référence à l'attribut @generate:id
de la variable
@name
référençant le nom de la variable.
Dans ce second cas, l'élément <generate:redefine-variable/>
doit être placé dans le scope de la variable.
Exemple, le code suivant :
<generate:redefine-variable name="toto" select="'bar'" /> <xsl:variable name="toto" select="'foo'" />
Devient, après compilation :
<xsl:variable name="toto" select="'bar'" />
Element generate:import
Ajoute un élément
<xsl:import/>
à la XSL
générée.
Element generate:param
Ajoute un élément
<xsl:param/>
global à la XSL
générée.
Element generate:target-block
Permet d'insérer un bloc de code contenu dans la XSL générée sans impact sur l'éxécution de la XSL source.
Exemple, le code suivant :
<xsl:variable name="foo" select="'foo'" /> <generate:target-block> <xsl:variable name="bar" select="'bar'" /> </generate:target-block>
Devient, après compilation :
<xsl:variable name="foo" select="'foo'" /> <xsl:variable name="bar" select="'bar'" />
Le contenu du <generate:target-block/>
étant enfant
d'un élément étranger au namespace XSL, il est ignoré par le processeur
lors de l'éxucution de la XSL source.
Contrôle des modes de compilation
Attribute generate:use-build-mode
Permet de spécifier le mode de génération à utiliser lors de la
pré-évaluation d'un appel de template (<xsl:call-template/>
)
ou d'une copie de template. (<generate:copy-template/>
)
Structures de contrôle du langage d’annotations
Element generate:for-each
Itère sur chaque élément de la séquence exprimée dans l'attribut
@select
.
La valeur de cet attribut est évaluée dans le contexte de génération.
<xsl:*/>
à l'intérieur du
bloc sont dupliqués, en tenant compte des <generate:*/>
et
@generate:*
qu'ils contiennent.
<generate:*/>
sont exécutées.
Element generate:if
@test
est vraie,
<xsl:*/>
à l'intérieur du bloc sont dupliqués, en tenant
compte des <generate:*/>
et @generate:*
qu'ils contiennent.
<generate:*/>
sont exécutées.
La valeur de l'attribut @test
est évaluée dans le
contexte de génération.
Element generate:choose
TODO
Utiliser des variables et paramètres dans le contexte de génération
Attribute generate:use
Associé à un <xsl:import/>
ou à une
<xsl:variable/>
permet de rendre ce·tte dernier·e visible
dans le contexte de
génération.
Exemple :
<xsl:variable name="foo" generate:use="yes" select="('foo','bar','baz')" /> <xsl:variable name="toto" generate:build="#all" select="count($foo)" /> <!-- Pour pré-évaluer $toto, $foo doit être visible dans le contexte de compilation. -->
Element generate:use-import
Importe une Transformation XSL externe et la rend visible dans le contexte de génération.
@href
contenant l'URI de la feuille de style importée
@id
, traité comme aune référence à
l'attribut @generate:id
d'un import déclaré la
XSL source
Les variables, paramètres, templates, fonctions... déclarées dans la XSL importée sont visibles dans le contexte de génération
Il appartient au développeur de s'assurer que les expressions XPath exprimées dans la XSL importée sont effectivement evaluables dans le contexte de génération.
L'utilisation de @generate:use
ou <generate:use-variable/>
peut, par exemple, être néscéssaire.
Element generate:use-param
Permet d'utiliser un paramètre visible uniquement dans le contexte de génération.
Element generate:use-variable
Définit une variable évaluée et visible dans le contexte de génération.
Exemple :
<xsl:use-variable name="foo" select="('foo','bar','baz')" /> <xsl:variable name="toto" select="14" /> <generate:redefine-variable name="toto" select="count($foo)" />
Identifier une instruction XSL
Attribute generate:id
Permet d'attribuer un identifiant à un élément <xsl:*/>
.
Cet identifiant peut être utilisé pour le designer dans une
annotation<generate:*/>
.
Exemple :
<xsl:template generate:id="bar" match="/" > <!-- (...) --> </xsl:template> <generate:remove idRef="bar" />