A prima vista il modo di procedere descritto nella lezione precedente può apparire confuso e anarchico; tuttavia bastano alcune considerazioni per convincersi del contrario.
Innanzitutto con questo modo di procedere si ottiene una maggiore modularità: potremmo demandare a terzi la creazione di un template per un insieme di nodi (ad esempio i tag XHTML), includerlo nel foglio di stile senza nemmeno guardarlo attraverso gli elementi include e import e avere cura di inoltrare ad esso i contenuti che deve trattare.
I nodi che dovessero sfuggire all'elaborazione possono essere individuati con il template “pigliatutto" della lezione precedente oppure, in assenza di quest'ultimo, andranno semplicemente persi senza produrre alcun effetto indesiderato.
Infine questa strategia ricalca il modello ad eventi della programmazione ad oggetti, dal momento che separa la generazione dell'evento dalla sua risoluzione, consentendo ai vari gestori (nel nostro caso i template) di agire indipendentemente l'uno dall'altro.
Supponiamo di estendere il formato sorgente con un nuovo elemento note, per contenere appunti personali distinti secondo il nome dell’autore da un attributo by, specificato dal frammento di DTD che segue (esempio4.xml):
<!ELEMENT mailbox (message*, note*)>
…
<!ELEMENT note ANY>
<!ATTLIST note
by CDATA #REQUIRED
>
A questo punto vorremmo poter definire il layout generico della pagina e lasciare a due collaboratori l’incombenza di decidere come debba essere visualizzato un appuntamento piuttosto che un appunto, ricevendo in risposta una coppia di
fogli di stile message.xsl e note.xsl. A questo proposito utilizzeremo
apply-templates selezionando gli elementi
da inoltrare ai template che (supponiamo) essere presenti nei file esempio4.xsl:
...
<tr>
<td>
<!-- spazio per i messaggi -->
<xsl:apply-templates select="//message" />
</td>
<td>
<!-- spazio per gli appunti -->
<xsl:apply-templates select="//note" />
</td>
</tr>
...
<xsl:include href="message.xsl" />
...
Il primo foglio di stile viene inserito con l’elemento include. La posizione nella quale verrà posto quest’ultimo è significativa, dal momento che i template esterni avranno lo stesso ordine di precedenza degli altri, ovvero dal basso verso l’alto. Se posto in fondo al foglio di stile avrà quindi la precedenza assoluta sugli altri template; è mia consuetudine includere i template esterni in questo modo, appena prima del template “pigliatutto".
La posizione dell’elemento import è al contrario tassativa; dev’essere infatti il primo figlio dell’elemento stylesheet che racchiude l’intero documento:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="note.xsl"/>
...
I template in esso contenuti avranno inoltre una precedenza inferiore rispetto a quelli ospitati dal foglio di stile primario. Dal momento che in fondo a quest’ultimo è presente un template che cattura tutti i nodi che non vengono altrimenti gestiti, vi è la sicurezza che nessun template incluso con import è destinato ad avere effetto.
Per proseguire sarà necessario distinguere i template che eseguono la copiatura dei contenuti da quelli che racchiudono invece la logica di composizione della pagina; questo è possibile definendo la modalità dei template attraverso l’attributo mode, che può assumere un valore arbitrario. Nel nostro caso ho scelto l’etichetta “copy":
<xsl:template match="content" mode="copy">
<xsl:apply-templates mode="copy" />
</xsl:template>
<xsl:template match="*|@*|text()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()" mode="copy"/>
</xsl:copy>
</xsl:template>
In questo modo solo i nodi che verranno selezionati con questa modalità potranno essere catturati dai template, questi ultimi di conseguenza non andranno quindi a nascondere quelli importati. Conseguentemente anche gli elementi apply-templates nel template contenuto in message.xsl andrà modificato:
...
<xsl:apply-templates select="content" mode="copy" />
...
Vedremo a qesto punto il template contenuto in note.xsl avere effetto sulla pagina HTML finale, come se avessimo utilizzato include. Sorge quindi spontaneo chiedersi quale ragione motivi il lavoro eseguito per rendere efficace import (ovvero la specifica della modalità) e la rigidità del suo utilizzo (la posizione obbligatoria in cima al documento).
Questa risiede nella possibilità di organizzare due o più template senza che questi si escludano vicendevolmente (il caso di template con il medesimo attributo match) secondo uno schema affine all’ereditarietà nei linguaggi di programmazione ad oggetti. Volessimo ad esempio mantenere la formattazione scelta dal collega, espressa in note.xsl, ma dividere i singoli appunti con una riga orizzontale, siamo in grado di prevaricare il template definito da quest’ultimo e richiamarlo invece all’occorrenza (esempio4.xsl).
...
<xsl:template match="note">
<tr>
<td>
<hr width="100%" />
</td>
</tr>
<xsl:apply-imports />
</xsl:template>
...
Trovandosi nel foglio di stile primario, questo template oscurerà il template presente in note.xsl e possiamo quindi creare una riga di tabella con il tag <hr> che vogliamo frapporre tra i singoli appunti.
Quando invece vogliamo cedere il passo al template esterno, invocheremo apply-imports, che possiede la stessa semantica di apply-templates ma considera solo i template esterni inclusi con import. Questa la pagina HTML risultante.