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/>

Le mode par défault est #default
#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.

Son utilisation se fait en deux étapes : Schema de fonctionnement

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

Son utilisation est basée sur un fichier décrivant des scenari de compilation.
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 XSLT

Attribute generate:build

Cet attribut sert à indiquer que l'on souhaite pré-évaluer l'instruction.

Valeurs :

Mode(s) de génération dans lesquels l'instruction doit être pré-évaluée.
Il peut s'ajouter :
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

Duplique dans la XSL générée un <xsl:template/> de la XSL source identifié soit par :
Une référence à son éventuel attribut@generate:id exprimée sous forme d'un attribut @idRef
Une référence à son @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
Cet élément accepte les enfants <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

Redéfinit une variable déclarée dans la XSL source identifiée par :
Un attribut @id, traité comme référence à l'attribut @generate:id de la variable
Un attribut @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.

A chaque itération :
Les éléments <xsl:*/> à l'intérieur du bloc sont dupliqués, en tenant compte des <generate:*/> et @generate:* qu'ils contiennent.
les instructions <generate:*/> sont exécutées.

Element generate:if

Si la condition exprimée dans @test est vraie,
Les éléments <xsl:*/> à l'intérieur du bloc sont dupliqués, en tenant compte des <generate:*/> et @generate:* qu'ils contiennent.
les instructions <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.

La XSL cible peut être référencée :
Via un attribut @href contenant l'URI de la feuille de style importée
Via un attribut @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.

TODO, non implémenté !

Element generate:use-variable

Définit une variable évaluée et visible dans le contexte de génération.

La/les expressions XPath exprimées pour définir la valeur de la variable doivent être effectivement évaluables 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" />