- Template XSLT per cicli e come funzioni ricorsive -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario Un qualunque editor testuale e Internet Explorer, Opera o Firefox oppure un server che supporti ASP .Net o PHP
Uso avanzato dei template XSLT

USARE I TEMPLATE COME FOSSERO FUNZIONI
Gli elementi xsl:call-template, xsl:param e xsl-with-param

Nel precedente articolo su XSLT abbiamo introdotto il concetto di template, ora ci proponiamo di approfondirlo e di evidenziare la sua utilità in analogia con quella che in un normale linguaggio di programmazione sarebbe una funzione. Lavorando in XSLT infatti capiterà di frequente di avere necessità di utilizzare funzioni non disponibili in XPath, in quanto fornisce solo alcune basiche funzioni di conversione, di logica booleana, di elaborazione di numeri e stringhe e altre specifiche per muoversi nel documento XML di origine.
Supponiamo dunque di aver bisogno di ottenere il codice esadecimale di un carattere, di dover estrarre la radice quadrata o anche solo dover ripetere più volte lo stesso codice (XSLT offre solo for-each, non è possibile ad esempio ripetere 3 volte la stessa stringa): come si potrebbe fare? In questi casi è necessario utilizzare i template come fossero funzioni. Per fare questo bisogna prima di tutto creare un template (xsl:template) che non abbia l'attributo match (e che non sia quindi associato ad una certa espressione XPath) ma che sia dotato di un nome (attributo name) attraverso il quale possa essere richiamato tramite xsl:call-template. Vediamo un esempio:


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:call-template name="scriviCiao" />
        <xsl:call-template name="scriviCiao" />
        <xsl:call-template name="scriviCiao" />
    </xsl:template>
    <xsl:template name="scriviCiao">
        <div>Ciao!</div>
    </xsl:template>
</xsl:stylesheet>

In questo codice abbiamo due template, uno principale associato al nodo radice (dal quale dunque inizierà l'elaborazione) e un altro chiamato scriviCiao. Come si può vedere il primo template attraverso l'elemento xsl:call-template richiama l'elaborazione del secondo template (specificandone il nome). In questo semplice esempio dunque l'output sarà come segue:


<div>Ciao!</div>
<div>Ciao!</div>
<div>Ciao!</div>

Un template utilizzato in questa maniera, proprio come una funzione di qualsiasi linguaggio di programmazione ordinario, può ricevere anche dei parametri che vanno dichiarati tramite xsl:param all'inizio del template (opzionalmente con valori di default) e passati durante una chiamata tramite elementi xsl:with-param all'interno di xsl:call-template. Ecco un esempio per comprendere meglio:


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:call-template name="saluta">
            <xsl:with-param name="soggetto" select="'Carlo'" />
            <xsl:with-param name="oggetto">Paolo</xsl:with-param>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="saluta">
        <xsl:param name="soggetto" select="'nessuno'" />
        <xsl:param name="oggetto">nessuno</xsl:param>

        <div><xsl:value-of select="$soggetto" /> saluta <xsl:value-of select="$oggetto" />!</div>
    </xsl:template>
</xsl:stylesheet>

Come si può vedere all'inizio del template saluta vi sono due elementi xsl:param con un attributo name che identifica il nome del parametro; il primo dei due è dotato anche di un attributo select che contiene un'espressione XPath , mentre il secondo contiene del testo: si tratta di due metodi differenti per impostare il valore di default. Inoltre il template saluta contiene due elementi xsl:value-of che richiamano l'espressione XPath $soggetto e $oggetto. Da questo si può intuire che per richiamare il valore di un parametro in XPath basta utilizzare il suo nome preceduto dal segno del dollaro.
L'ultima differenza con l'esempio precedente è che nel primo template, l'elemento xsl:call-template contiene i due parametri espressi tramite xsl:with-param. Per xsl:with-param va sempre specificato l'attributo name per indicare di quale parametro si vuole impostare il valore che può essere definito tramite un'espressione XPath nell'attributo select o direttamente tra il tag di apertura e quello di chiusura.

USO RICORSIVO DEI TEMPLATE PER REPLICARE UN CICLO
Come aggirare le limitazioni di XSLT: estrazione della radice quadrata.

Una delle possibilità più utili che servirsi dei template come fossero funzioni offre è effettuare chiamate ricorsive (ovvero far sì che il template richiami se stesso), tecnica che in sostanza supplisce alla mancanza di un ciclo che non sia restrittivo come xsl:for-each. Vediamo un esempio:


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

    <xsl:template match="/">
        <xsl:call-template name="ripeti">
            <xsl:with-param name="volte">13</xsl:with-param>
            <xsl:with-param name="cosa">Ciao a tutti!</xsl:with-param>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="ripeti">
        <xsl:param name="volte" />
        <xsl:param name="cosa" />
        <xsl:param name="contatore">1</xsl:param>

        <div><xsl:value-of select="$cosa" /></div>

        <xsl:if test="$contatore != $volte">
            <xsl:call-template name="ripeti">
                <xsl:with-param name="volte" select="$volte" />
                <xsl:with-param name="cosa" select="$cosa" />
                <xsl:with-param name="contatore" select="$contatore + 1" />
            </xsl:call-template>
        </xsl:if>

    </xsl:template>
</xsl:stylesheet>

Il primo template richiama ripeti (template che, come intuibile, ripeterà uno stesso testo un dato numero di volte) specificando come parametro volte 13 e come parametro cosa un testo di esempio. Si noti che non viene specificato il parametro contatore, il quale assumerà dunque il suo valore di default, cioè 1.
Il template ripeti stamperà dunque una prima volta il valore del parametro cosa e poi verificherà (tramite un xsl:if) se il parametro contatore è differente dal numero di volte che si desidera che il testo sia ripetuto (parametro volte): se è differente il template richiama se stesso con gli stessi parametri ricevuti ma aggiungendo 1 al parametro contatore. In questo modo tutto il processo continuerà a ripetersi fino a quando il parametro contatore non eguaglierà il parametro volte, concludendo così il ciclo.
Vediamo un'applicazione matematica di questo metodo: l'estrazione della radice quadrata.


<!--
    Tratto da un template di Nate Austin per l'estrazione 
    della radice quadrata con il metodo di Isaac Newton
-->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
        <xsl:call-template name="radiceQuadrata">
            <xsl:with-param name="numero">2</xsl:with-param>
            <xsl:with-param name="precisione">15</xsl:with-param>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="radiceQuadrata">
        <xsl:param name="numero" />
        <xsl:param name="precisione">10</xsl:param>
        <xsl:param name="risultato">1</xsl:param>
        <xsl:param name="contatore">1</xsl:param>
        <xsl:choose>
            <xsl:when test="$risultato * $risultato = $numero or $contatore > $precisione">
                <xsl:value-of select="$risultato " />
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="radiceQuadrata">
                    <xsl:with-param name="numero" select="$numero" />
                    <xsl:with-param name="risultato" 
                        select="$risultato - (($risultato * $risultato - $numero) div (2 * $risultato))" />
                    <xsl:with-param name="contatore" select="$contatore + 1" />
                    <xsl:with-param name="precisione" select="$precisione" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Il template radiceQuadarta può ricevere 4 parametri di cui però metà (risultato e contatore) sono solo ad uso interno, infatti in xsl:call-template vengono passati solamente il numero di cui estrarre la radice e la precisione con cui calcolarlo. L'algoritmo in questione è stato studiato da Newton e si basa sul continuare ad applicare una stessa formula ottenendo così un risultato man mano sempre più preciso. Il parametro precisione indica il numero di volte che questa formula deve essere applicata, il parametro risultato è il numero ottenuto che diviene man mano più preciso e alla fine stampato e il parametro contatore è quello che viene incrementato ad ogni chiamata ricorsiva per interrompere l'esecuzione quando si sta per eccedere la precisione desiderata. In alternativa il template smette di richiamare se stesso se il risultato moltiplicato per se stesso dà il numero originale, ovvero se si è giunti al risultato esatto.
La formula di Newton in questione è la seguente (dove R è il numero di cui si sta ricercando la radice quadrata): Xn = (Xn-1 * Xn-1 - R) / (2 * Xn-1).

SOLUZIONI ALTERNATIVE
Script interni e esterni, librerie dei parser e XPath 2 e XSLT 2.

Certamente implementare l'algoritmo di estrazione della radice quadrata in XSLT è dispendioso in risorse (effettuare un grande numero di chiamate ricorsive non è mai una buona idea) e in tempo (dato l'obbligo di utilizzare template che si richiamano ricorsivamente ogni qualvolta è necessario un ciclo), ma d'altro lato le alternative offrono notevoli svantaggi. Alcuni parser permettono di scrivere proprie funzioni XPath sia all'interno che all'esterno del documento XSLT, spesso in JavaScript (anche se con .Net di Microsoft è possibile utilizzare qualsiasi linguaggio della piattaforma), ma in realtà gli script all'interno di XSLT non sono molto supportati, o comunque in quanto non standard sono sempre legati ad una piattaforma (il che comporta la perdita di tutti i vantaggi che offre XML e quindi XSLT). Altra soluzione è utilizzare vere e proprie estensioni per il parser, ma anche in questo si finisce per legarsi ad un solo parser. L'unica vera alternativa è rivolgersi a XPath 2 e XSLT 2.

 

<< INDIETRO by VeNoM00