Università Federico II di Napoli Dipartimento di Scienze Matematiche, Fisiche e Naturali
Corso di Laurea in Informatica
Anno Accademico 2006/2007
Tesi: “Sviluppo di un software per la creazione automatica di documenti XML ricorsivi DTD based” Relatore: Prof. Francesco Cutugno Prof. Leandro D’Anna
Laureando: Raffaele Liguoro Mat. 566/1161
- 2 -
Indice Introduzione pag.3 CAPITOLO 1 - Analisi critica degli strumenti pag.4 1.1 Lo standard XML pag.5 1.1.1 Origini e scopi del linguaggio XML pag.5 1.1.2 Regole sintattiche pag.6 1.1.2.1 Intestazione di un documento XML pag.6 1.1.2.2 I tag XML pag.6 1.1.2.3 Gli elementi XML pag.7 1.1.2.4 Gli attributi XML pag.8 1.1.2.5 I namespaces pag.9 1.1.2.6 Referenze ad entità pag.12 1.1.3 La famiglia di XML pag.12 1.2 Le DTD pag.15 1.2.1 Nozioni base sulle DTD pag.15 1.2.2 Validazione di documenti pag.18 1.3 XPATH pag.20 1.3.1 Origini e scopi di XPATH pag.20 1.3.2 Modello dati XPATH pag.21 1.3.3 Sintassi di XPATH pag.25 1.3.3.1 Abbreviated e full syntax pag.25 1.3.3.2 Axis pag.26 1.3.3.3 Test node pag.28 1.3.3.4 Predicates pag.29 1.3.4 Funzioni ed espressioni XPATH pag.30 1.3.5 Esempi pratici pag.32 1.4 XQUERY pag.35 1.4.1 Origini e scopi di XQUERY pag.35 1.4.2 Interrogazione di documenti XML pag.36 1.4.3 Espressioni FLWOR pag.37 1.4.4 Espressioni condizionali pag.40 1.4.5 Funzioni predefinite pag.41 1.4.6 Funzioni personalizzate pag.42 1.4.7 Differenze tra XQUERY ed XPATH pag.43 CAPITOLO 2 - Aspetti progettuali del software pag.44 2.1 Analisi del problema pag.45 2.2 Motivazioni pag.45 2.3 Progettazione astratta pag.45 2.4 Scelta del linguaggio pag.46 2.4.1 Il linguaggio C# pag.46 2.4.2 Differenze con altri linguaggi pag.47 2.4.3 Esempi di utilizzo del C# pag.48 2.4.4 Standardizzazione pag.49 2.5 Struttura logica dell’applicazione pag.50 CAPITOLO 3 - Descrizione del software pag.57 3.1 Descrizione generale pag.58 3.2 Generazione di un documento XML di esempio pag.58 3.3 Analisi teorica dei parametri settabili pag.61 3.4 Settaggio pratico dei parametri pag.62 3.5 Valutazione dell’XML generato pag.67 3.6 Lo strumento XQuery resolver pag.70 3.7 Altre funzionalità pag.71 CAPITOLO 4 – Testing e valutazioni pag.73 4.1 Valutazione delle prestazioni spaziali e temporali del software pag.74 4.2 Valutazione dell’output generato pag.75 4.3 Verifica dei risultati ottenuti tramite il software XGATE pag.80 Conclusioni pag.89 APPENDICE A : Caratteristiche tecniche del software pag.90 APPENDICE B : Glossario delle abbreviazioni e dei termini tecnici pag.92 APPENDICE C : Estratti di codice sorgente pag.96 Bibliografia pag.104 Ringraziamenti pag.105
- 3 -
Introduzione
La sempre maggior diffusione dello standard XML avvenuta negli ultimi tempi, derivata
dalla scoperta delle potenzialità di quest’ultimo e da un’intrinseca semplicità di utilizzo, ha
aperto la strada ad uno sviluppo sempre crescente di applicazioni che fruiscono dell’XML
per svariati impieghi. Tali impieghi spaziano dall’editing di semplici documenti XML alla
manipolazione di database contenenti strutture complesse ed eventualmente ricorsive
difficilmente gestibili con altri tipi di applicazioni che non adoperano tale standard.
Il programma XML recursive generator è un tool ideato ed implementato allo scopo di
permettere la generazione di documenti XML in maniera pseudo-casuale, seguendo
vincoli di generazione dettati da parametri impostati dall’utente tramite un’interfaccia
grafica (GUI). Allo stesso tempo il software rispetta pedissequamente lo schema di
generazione dettato da una qualsiasi DTD selezionabile anch’essa dall’utente. In altre
parole, il software è stato concepito allo scopo di generare in maniera “controllata” un
documento XML seguendo lo schema estrapolato da una DTD e seguendo una serie di
parametri che regolano diversi fattori, quali: il numero di nodi da generare nel documento,
le dimensioni (altezza, larghezza) dell’albero XML da generare, i livelli entro i quali
possono essere generati i nodi ricorsivi, la possibilità di effettuare ricorsioni dirette e/o
indirette solo su determinati nodi dell’albero, nonchè di regolare indipendentemente le
percentuali delle sopracitate ricorsioni rispetto al numero totale dei nodi da generare.
Il software permette inoltre all’utente di visualizzare graficamente i risultati dettagliati della
generazione effettuata e di salvarli su file (sempre in formato XML) per effettuare confronti
con risultati di generazioni eseguite precedentemente, allo scopo di permettere all’utente
la realizzazione di opportuni benchmark sui documenti XML generati. Le tecnologie di
base utilizzate nel progetto sono DotNET 2.0 e Data Oject Model, incluse nella piattaforma
Visual Studio .NET. La scelta del linguaggio di programmazione utilizzato per la creazione
del software è caduta sul C#, un potente successore del ben noto C++, il quale fornisce
numerosi strumenti per la manipolazione di strutture e dati XML, ed è inoltre affiancato da
un versatile compilatore grafico in grado di facilitare la creazione di interfacce e finestre.
Tutte queste caratteristiche e potenzialità mirano dunque a rendere XML recursive
generator un software completo per la creazione, lo studio ed il testing di documenti XML
ricorsivi.
- 4 -
Capitolo
1 Analisi critica degli strumenti
- 5 -
1.1 Lo standard XML
1.1.1 Origini e scopi del linguaggio XML
XML è un formato di rappresentazione delle informazioni il cui acronimo sta per
eXtensible Markup Language che tradotto letteralmente vuol dire “Linguaggio estendibile
basato su marcatori”. XML nasce nel febbraio del 1998, come raccomandazione del W3C
(World Wide Web Consortium), ed ha come scopo la rappresentazione di informazioni
strutturate di qualunque natura, basate su precise regole di rappresentazione.
XML ha origine dal metalinguaggio SGML (Standard Generalized Markup Language), che
in quanto tale è semplicemente un insieme di regole generalizzate usate per creare
molteplici linguaggi particolari: i Markup Languages.
XML è anch’esso un metalinguaggio, che ha come scopo la rappresentazione di contenuti
testuali organizzati gerarchicamente.
Con esso è dunque possibile rappresentare qualsiasi tipo di contenuto che sia
riconducibile ad una gerarchia di informazioni. Punti di forza dell’XML sono la sua
semplicità e l’immediata leggibilità, dovuta proprio all’utilizzo del puro testo come metodo
di scambio per le informazioni. Esso prevarica sugli altri formati puramente testuali per il
concetto intrinseco di organizzazione dovuto alla presenza dei marcatori, che permettono
di definire una universale organizzazione dei dati valida per tutti gli utilizzatori, al contrario
dei formati di testo semplice che per essere scambiati tra diversi interlocutori hanno
bisogno di precise regole di formattazione da concordare a priori.
Con l’avvento dello standard XML 1.1 (24 ottobre 2001) diventa possibile pensare ad XML
non pìu solamente come ad un documento di testo gerarchico, ma, astraendo dal
contenuto fisico del testo stesso, è possibile concentrarsi sul contenuto informativo da
esso rappresentato.
L’utilizzo del testo puro è in sè anche la causa di uno dei principali problemi dell’XML: la
scarsa economicità in termini di spazio di occupazione.
I documenti XML sono ingombranti perchè appunto testuali, quindi richiedono tempi
maggiori per essere trattati e trasferiti via internet.
Tuttavia anche questo tipo di standard ha il suo campo di applicazione, ed esamineremo
accuratamente come sia possibile trarre dal suo utilizzo i maggiori benefici.
- 6 -
1.1.2 Regole sintattiche
Per far si che XML possa essere leggibile su qualsiasi piattaforma è necessario che
vengano rispettate una serie di regole sintattiche di base, le quali esamineremo ora in
dettaglio.
1.1.2.1 Intestazione di un documento XML
Ogni documento XML deve presentare un header, costituito da diverse informazioni, di cui
alcune opzionali. Ecco un esempio:
<?xml version="1.0" encoding=”ISO-8859-1” standalone=”yes” ?>
La precedente riga contiene l’identificatore principale del documento XML, la versione
utilizzata (1.0), la codifica dei caratteri (ISO-8859-1) e l’attributo “standalone” impostato su
“yes” il quale indica che il presente documento è indipendente da altri. Solo il primo dei
precedenti attributi è obbligatorio.
1.1.2.2 I tag XML
Un documento XML è simile ad uno HTML, in cui però possiamo scegliere di creare tag a
nostro piacimento. Ecco un esempio:
<nota> <da>Gianni</da> <a>Mario</a> <titolo>Messaggio</titolo> <testo>Ciao</testo> </nota>
La scelta dei tag può essere effettuata a seconda delle informazioni che ci interessa
rappresentare e che la specifica applicazione dovrà riconoscere. Il primo tag <nota>
identifica la radice del documento. I tag specificano il contenuto della nota in termini di
titolo, mittente, destinatario e testo. L’ultimo tag conclude la descrizione della nota.
I tag si dividono in:
- 7 -
• Tag di apertura: <nometag> es: <nota>
• Tag di chiusura: </nometag> es: </nota>
• Tag vuoti: <nometag/> es: <nota/>
Inoltre bisogna ricordare che XML è un metalinguaggio case sensitive: l'elemento <nota>
è diverso dall'elemento <Nota> e cosi via.
1.1.2.3 Gli elementi di XML
Gli elementi in XML sono estendibili, in questo modo è possibile mantenere compatibilità
con versioni precedenti (backward compatibility) ad esempio:
<?xml version="1.0"?> <nota> <da>Gianni</da> <a>Mario</a> <titolo>Saluti</titolo> <messaggio>Ciao</messaggio> </nota> <?xml version="1.0"?> <nota> <da>Gianni</da> <a>Mario</a> <titolo>Saluti</titolo> <messaggio>Ciao</messaggio> <data>15/07/2007</data> </nota>
Gli elementi in XML sono in relazione tra di loro e queste relazioni determinano il modello
del documento. Il documento è organizzato come un albero, in cui la relazione di
contenimento tra tag è equivalente alla relazione nodo-sottonodo, ad esempio i tag: <a>,
<da>, <titolo> e <messaggio> sono sottonodi o nodi-figlio di <nota>.
L’unico nodo che non è figlio di nessuno (ad esempio: <nota>) è la cosiddetta entità
radice.
In XML è inoltre obbligatorio che tutti gli elementi debbano possedere un tag di chiusura.
E’ importante rispettare la sequenza di apertura/chiusura dei tag; l'ordine di chiusura dei
tag deve sempre essere inverso rispetto all'ordine di apertura; il successivo è un esempio
di uso errato della sintassi degli elementi:
<Mittente><Destinatario>Gianni</Mittente></Destinatario>
- 8 -
1.1.2.4 Gli attributi di XML
In XML gli attributi vengono utilizzati per aggiungere caratteristiche o proprietà agli
elementi. Un attributo è una coppia nome-valore, separata da un segno di uguale, che può
essere presente all'interno del tag di apertura di un elemento, dopo il nome dell'elemento
stesso. Un elemento può contenere uno o più attributi. Ogni attributo è sempre associato
ad un elemento, non può essere indipendente da esso, ed inoltre, lo stesso attributo può
ricorrere anche in molteplici elementi. Gli attributi sono informazioni aggiuntive che
possono essere inserite negli elementi XML per completarne o arricchirne l’informazione,
possono essere racchiusi sia tra apici singoli che doppi, ad esempio:
<mela colore="rossa"> Ottima da mangiare </mela> oppure:
<mela colore=’rossa’> Ottima da mangiare </mela>
Spesso le stesse informazioni possono essere rappresentate sia tramite attributi che
tramite (sotto)elementi. Ad esempio: <nota> <da>Gianni</da> <a>Mario</a> <titolo>Test</titolo> <messaggio>...</messaggio> </nota>
oppure:
<nota titolo="Test"> <da>Carlo</da> <a>Luca</a> <messaggio>...</messaggio> </nota>
La scelta tra attributi o elementi è soggettiva, tuttavia le due soluzioni non sono in genere
equivalenti. Utilizzando gli attributi possono presentarsi i seguenti problemi:
- 9 -
• non possono contenere valori multipli:
<padre nome=“Gianni”><figlio>Luca</figlio><figlio>Marco</figlio></padre>
• sono difficilmente espandibili (aggiunta di sottoelementi);
• non possono descrivere strutture:
<libro><autore><nome> ... </nome><cognome> ... </cognome></autore></libro>
• non hanno un supporto standard per la gestione nei programmi;
• sono difficili da controllare rispetto ad un formato di documento DTD;
E’ opportuno usare gli attributi per informazioni essenziali per l’elemento, come ad
esempio gli identificativi (ID).
1.1.2.5 I namespaces
Nella visione XML, i tipi di documenti si mescolano e si fondono tra loro in maniera
complessa.
Lo stesso documento potrebbe avere alcuni elementi definiti in un vocabolario ed altri in
un altro.
Un esempio comune è un documento XML di valori di borsa che adopera i tag di HTML
per definire gli elementi di testo, ed un insieme di tag specifico per gli elementi di borsa,
oppure anche un documento che contiene elementi strutturali di un vocabolario principale.
I problemi che nascono riguardano il compito di identificare esattamente l'ambito di
ciascun elemento, conciliare la presenza di elementi definiti in uno di più vocabolari, e
soprattutto conciliare la presenza di elementi definiti con lo stesso nome in più vocabolari
diversi.
I namespace in XML si propongono per risolvere questi problemi.
XMLNamespaces è una recommendation di W3C del 1999.
- 10 -
Supponiamo che una libreria voglia mettere il proprio database su Web:
<h:html xmlns:mia="http://www.mia.it/libri” xmlns:h="http://www.w3.org/HTML/1998/html4"> <h:head><h:titolo>Sezione Informatica</h:titolo></h:head> <h:body> <mia:Sezione Informatica> <mia:titolo>Guida ad XML</mia:titolo> <h:table> <h:tr> <h:td>Autore</h:td> <h:td>Prezzo</h:td> <h:td>Pagine</h:td> <h:td>Data</h:td> </h:tr> <h:tr> <h:td><mia:autore>Tizio</mia:autore></h:td> <h:td><mia:prezzo>99</mia:prezzo></h:td> <h:td><mia:pagine>999</mia:pagine></h:td> <h:td><mia:editore>XXX</mia:editore></h:td> </h:tr> </h:table> </mia:Sezione Informatica> </h:body> </h:html>
Ogni nome di elemento o attributo del documento XML è preceduto da un prefisso che ne
specifica l'ambito.
Il prefisso è separato dal carattere ‘:’ dal nome dell’elemento o dell’attributo.
L’attributo predefinito "xmlns" serve per introdurre i prefissi usati dai namespace del
documento.
Il valore dell’attributo è un URI che non ha nessun valore dichiarativo, ma solo informativo.
Si usa un URI perché si sa già che è unico su Internet.
Poiché ogni namespace userà un prefisso diverso, è possibile capire quali elementi
appartengono all’uno e all’altro, e di evitare qualunque problema di collisione.
Un nome (di elemento o di attributo) che contiene prefisso di namespace e nome locale
viene detto "nome qualificato".
Ovviamente la natura specifica del prefisso è irrilevante, conta solo che sia associato ad
un URI ben preciso.
Nella dichiarazione xmlns si pone il nome del prefisso che si intende usare nel corso del
documento per gli elementi definiti in quel namespace.
L’assenza di tale prefisso in xmlns indica la presenza di un namespace di default, per cui
tutti i nomi privi di prefisso si debbono intendere appartenenti a quel namespace.
Inserendo “HTML” come namespace di default, è tutto un po’ più leggibile, riprendendo
l'esempio precedente avremo:
- 11 -
<html xmlns:mia="http://www.mia.it/libri” xmlns="http://www.w3.org/HTML/1998/html4"> <head><titolo>Sezione Informatica</titolo></head> <body> <mia:Sezione Informatica> <mia:titolo>Guida ad XML</mia:titolo> <table> <tr><td>Autore</td> <td>Prezzo</td> <td>Pagine</td> <td>Data</td></tr> <tr> <td><mia:autore>Tizio</mia:autore></td> <td><mia:prezzo>99</mia:prezzo><td> <td><mia:pagine>999</mia:pagine><td> <td><mia:editore>XXX</mia:editore></td> </tr> </table> </mia:Sezione Informatica> <body> <html>
La dichiarazione di namespace può essere fatta ovunque, e ha scope solo all’interno
dell’elemento in cui è stata fatta.
Possono essere fatte più definizioni di namespace di default, quella interna ha ovviamente
precedenza.
Il namespace contiene tre partizioni di nomi:
• All Element Types Partition: contiene tutti i nomi degli elementi definiti nel
namespace; ogni nome è unico in questa partizione;
• The Global Attribute Partition: contiene i nomi di tutti gli attributi definiti come
globali, ovvero usabili ovunque sia definito il namespace;
• The Per-Element-Type Partitions: ogni nome della All Element Types Partition ha
un namespace locale dove sono definiti i nomi degli attributi non qualificati ciò
significa che gli attributi propri di un elemento non debbono ripetere il prefisso di
namespace:
- 12 -
1.1.2.6 Referenze ad entità
I dati di tipo carattere contenuti all’interno di un elemento non possono contenere siboli
come <,&,>,’,” che non siano sottoposti a escape, infatti, prendendo ad esempio il
simbolo “<”, esso viene sempre interpretato come inizio di un tag. Nel caso si abbia la
necessità di utilizzarlo all’interno di un testo bisogna sostituirlo con la sequenza < Una
simile sequenza viene detta referenza ad entità. Il parser che legge il documento si
occupe di rimpiazzare la sequenza di escape con il rispettivo carattere corrispondente.
XML predefinisce esattamente cinque referenze ad entità:
• < (carattere minore “<”)
• & (carattere e-commerciale “&”)
• > (carattere maggiore “>”)
• " (carattere virgolette “ “ ”)
• ' (carattere apostrofo “ ‘ ”)
1.1.3 La famiglia di XML
Dato che XML è un (meta)linguaggio per specificare altri linguaggi costituisce un livello
comune per il dialogo in ambienti differenti. XML non dice nulla su che tag utilizzare, ma
fissa solo delle regole comuni per eseguire correttamente il parsing del file. E’ possibile
usare XML per gli scopi più disparati,a seconda delle operazioni che verranno eseguite
dalla specifica applicazione di fronte agli specifici tag. Insieme ad XML abbiamo un
insieme di linguaggi derivati:
• XML Schema: serve per definire la grammatica di un documento XML, sostituto e
miglioramento rispetto al DTD, scritto in XML (XML Schema è un “dialetto” XML),
definito da Microsoft e ora standard W3C comunemente indicato come XSD
(acronimo di XML Schema Definition), non sono nient'altro che file di testo (con
estensione .xsd) che servono a definire elementi e attributi di un documento XML,
definire quali sono gli elementi figli e il loro ordine e numero, definire se un
elemento è vuoto oppure può contenere testo o altri elementi annidati, definire il
tipo, il valore di default o il valore fisso di elementi e attributi.
- 13 -
• XSL: acronimo di eXtensible Stylesheet Language, XSL è un linguaggio di
annotazione che serve a "trasformare" la struttura di un documento in formato XML
in un'altra struttura. Lo scopo principale di questa trasformazione è certamente
quello di "convertire" il documento XML in un formato adatto alla presentazione su
diversi supporti (per esempio, la sua trasformazione in HTML).
• XPath: XPath è un linguaggio tramite il quale è possibile esguire delle espressioni
per indirizzare parti di un documento XML; è un linguaggio ideato per operare
all'interno di altre tecnologie XML quali XSL e XPointer, ed è caratterizzato dal fatto
di avere una sintassi non XML. In questo modo può essere meglio utilizzato
all'interno di URI o come valore di attributi di documenti XML; XPath opera su una
rappresentazione logica del documento XML, che viene modellato con una
struttura ad albero, ed XPath definisce una sintassi per accedere ai nodi di tale
albero. Oltre a questo XPath mette a disposizione una serie di funzioni per la
manipolazione di stringhe, numeri e booleani, da utilizzare per operare sui valori o
sugli attributi dei nodi.
• XLink: XLink è un linguaggio basato su XML per definire dei link (collegamenti) tra
risorse; il concetto di link è già presente ed è ampiamente usato in HTML, grazie al
tag <a> con il quale è possibile inserire all'interno di una pagina HTML dei link a
delle risorse (pagine html, immagini, file, etc.) esterne o interne alla nostra pagina; il
link utilizzato in HTML risente però di alcune limitazioni, XLink nasce con l'intento di
ovviare ai tali limitazioni, con XLink siamo in grado di definire dei link multipli,
multidirezionali e caratterizzarli anche dal punto di vista semantico.
• XPointer: XPointer è l'acronimo di XML Pointer language, Xpointer è un linguaggio
ideato per indirizzare parti di un documento XML; con XPointer abbiamo uno
strumento per indirizzare precisi punti o porzioni di un documento; da notare che le
parti di documento sono linkate senza che si debba andare a modificare il
documento di destinazione, come avviene ad esempio in HTML; XPointer usa la
sintassi XPath, con qualche estensione, per identificare le parti di documento da
indirizzare.
- 14 -
• XQuery: XQuery è l'acronimo di XML Query language nasce proprio con l'intento di
realizzare un linguaggio per recuperare agevolmente le informazioni da un
documento XML ed andare a costituire una sorta di "SQL per XML"; XQuery non è
un linguaggio basato su XML ed è costituito da una sintassi semplice e facilmente
leggibile per formulare, nel modo più agevole possibile, le query sui dati; il working
group del W3C ha sviluppato anche una versione di XQuery con sintassi XML,
chiamata XQueryX.
Poichè lo sviluppo di estensioni a partire dalla specifiche di base XML continua tuttora I
possibili sviluppi futuri del linguaggio comprendono:
• Xfragment
Uno sforzo teso a rendere sensate delle porzioni di documenti XML che non
possono essere considerati documenti ben formattati se isolati dal resto del
documento.
• XML Schemas Un’applicazione XML in grado di descivere i contenuti leciti di documenti che
siano conformi ad un particolare vocabolario XML.
• XHTML Una riformulazione di HTML come applicazione XML ben formattata,
modulare e potenzialmente implementabile.
• XML Signatures
Un metodo standard per firmare in maniera digitale documenti XML,
permettendo l’autenticazione dei documenti risultanti.
• Canonical XML Un algoritmo standard utilizzato per verificare l’identicità di due documenti
XML.
- 15 -
1.2 Le DTD 1.2.1 Nozioni di base sulle DTD Una caratteristica fondamentale dell'XML è l'estensibilità.
L'autore di un documento XML può creare nuovi tag per descrivere i contenuti semantici
dei propri dati, semplificando il loro scambio fra i gruppi di persone interessate allo stesso
settore.
Ciò ha portato alla necessità di definire delle regole grammaticali, o vincoli, alle quali gli
elementi devono attenersi.
Queste regole grammaticali sono definite nelle specifiche XML e sono codificate nel DTD
(acronimo di Document Type Definition che significa definizione del tipo di documento.
Le regole grammaticali o vincoli specificano:
• quale è l'insieme degli elementi e degli attributi che si possono usare nel
documento XML;
• quali sono le relazioni gerarchiche fra gli elementi;
• quale è l'ordine in cui gli elementi appariranno nel documento XML;
• quali elementi ed attributi sono opzionali;
Quando un documento XML è ben formato e rispetta le regole della DTD a cui si riferisce
si dice che esso è un documento XML valido. In un documento XML si può specificare la
DTD in modo esplicito (DTD interna) o con un riferimento ad un documento distinto (DTD
esterna). Se la DTD è interna, deve essere dichiarata con la seguente sintassi:
<!DOCTYPE elemento-radice [dichiarazione-elementi]>
- 16 -
Esempio semplificato di una DTD per una legge: <?xml version="1.0"?> <!DOCTYPE Legge [ <!ELEMENT Legge (TitoloLegge,Articolato) > <!ELEMENT TitoloLegge (#PCDATA)> <!ELEMENT Articolato (Capo+)> <!ELEMENT Capo (Rubrica?,Articolo+)> <!ELEMENT Rubrica (#PCDATA)> <!ELEMENT Articolo (Rubrica?,Comma+)> <!ELEMENT Comma (#PCDATA)> <!ATTLIST Capo Num CDATA #REQUIRED> <!ATTLIST Articolo Num CDATA #REQUIRED> <!ATTLIST Comma Num CDATA #REQUIRED> ]> <Legge> <TitoloLegge>Disciplina delle associazioni a scopo benefico</TitoloLegge> <Articolato> <Capo Num="I"> <Rubrica>DISPOSIZIONI GENERALI</Rubrica> <Articolo Num="1"> <Rubrica>Finalità e oggetto della legge</Rubrica> <Comma Num="1">La Repubblica riconosce il valore sociale...</Comma>...
Se la DTD è esterna, deve essere dichiarata con la seguente sintassi:
<!DOCTYPE elemento-radice SYSTEM "nome-file">
Vediamo lo stesso esempio semplificato con la dichiarazione esterna: <?xml version="1.0"?> <!DOCTYPE Legge SYSTEM "legge.dtd"> <Legge> <TitoloLegge>Disciplina delle associazioni di promozione sociale <Articolato><Capo Num="I"> <Rubrica>DISPOSIZIONI GENERALI</Rubrica> <Articolo Num="1"> <Rubrica>Finalità e oggetto della legge</Rubrica> <Comma Num="1">La Repubblica riconosce il valore sociale...</Comma> ... </Legge>
dove legge.dtd è un file esterno che contiene la DTD per il documento XML, e cioè le
righe:
<!ELEMENT Legge (TitoloLegge,Articolato)> <!ELEMENT TitoloLegge (#PCDATA)> <!ELEMENT Articolato (Capo+)> <!ELEMENT Capo (Rubrica?,Articolo+)> <!ELEMENT Articolo (Rubrica?,Comma+)> <!ELEMENT Rubrica (#PCDATA)> <!ELEMENT Comma (#PCDATA)> <!ATTLIST Capo Num CDATA #REQUIRED> <!ATTLIST Articolo Num CDATA #REQUIRED> <!ATTLIST Comma Num CDATA #REQUIRED>
- 17 -
In una DTD gli elementi del documento XML sono definiti tramite una dichiarazione di
elemento che può assumere tre forme.
• Prima forma:
<!ELEMENT NomeElemento (#PCDATA)>
Questa è la forma più semplice in cui si dichiara che un elemento può contenere
come valore una qualsiasi stringa di testo; ad esempio:
<!ELEMENT Rubrica (#PCDATA)>
In questo caso il documento XML che fa riferimento al DTD contenente tale
dichiarazione potrà contenere un elemento del tipo:
... <Rubrica>TESTO</Rubrica> ...
• Seconda forma:
<!ELEMENT NomeElemento EMPTY>
Questa è la forma con la quale si dichiara che un elemento è privo di valore.
In questo caso all'elemento sono sempre di solito associati uno o più attributi:
<!ELEMENT Numero EMPTY> <!ATTLIST Numero Valore CDATA #REQUIRED>
e quindi il documento XML che fa riferimento alla DTD contenente tale
dichiarazione potrà contenere un elemento del tipo:
... <Numero Valore="1"/> ...
- 18 -
• Terza forma:
<!ELEMENT NomeElemento (ElementoFiglio1, ElementoFiglio2, ...)>
Questa è la forma a contenitore in cui si dichiara che un elemento è composto da
sotto-elementi (figli) con i quali s'instaura una stretta gerarchia del tipo padre-figlio:
<!ELEMENT Legge (TitoloLegge, Articolato)> <!ELEMENT TitoloLegge (#PCDATA)> <!ELEMENT Articolato (...)>
In questo caso il documento XML che fa riferimento al DTD contenente tale
dichiarazione potrà contenere un elemento del tipo:
<Legge> <TitoloLegge>Disciplina delle associazioni a scopo benefico</TitoloLegge> <Articolato> ... </Articolato> </Legge>
1.2.2 Validazione di documenti
Un documento per il quale è richiesta la validazione deve includere un riferimento a DTD
con il quale deve essere messo a confronto.
Questo riferimento deve essere fornito nella dichiarazione del tipo di documento
(document type declaration), che può avere ad esempio il seguente aspetto:
<!DOCTYPE person SYSTEM “http://indirizzo.org/person.dtd”>
Questa dichiarazione afferma che l’elemento radice è person e che la DTD può essere
rintracciata all’URI “http://indirizzo.org/person.dtd”.
URI è l’acronimo di Uniform Resource Identifier (identificatore uniforme di risorsa). Gli
URI sono un superset degli URL, perchè includono sia gli URL che gli URN (Uniform
resource names). Un URI in linea teorica permette di identificare una risorsa
- 19 -
indipendentemente dalla sua collocazione (infatti la risorsa potrebbe essere disponibile in
più locazioni differenti ma eugualmente autorevoli). Nella pratica invece gli URL sono
l’unico tipo di URI realmente utilizzati.
Una DTD può essere associata ad un public ID (nome ID pubblico). Il nome dell’ID
pubblico identifica univocamente l’applicazione XML utilizzata. Per indicare che si vuole
utilizzare un ID pubblico, è sufficiente utilizzare la parola chiave PUBLIC al posto di
SYSTEM. La seguente dichiarazione di tipo di documento fa riferimento al DTD
standardizzato da Netscape per l’applicazione RSS:
<!DOCTYPE res PUBLIC “-//Netscape Communications//DTD RSS 0.91//EN”
“http://my.netscape.com/publish/formats/rss-0.91.dtd”>
Nella pratica, gli ID pubblici non vengono utilizzati frequentemente, tutti i parser XML si
basano sull’URI per la validazione del documento.
- 20 -
1.3 XPATH
1.3.1 Origini e scopi di XPATH
XPath è un linguaggio tramite il quale è possibile eseguire delle espressioni per indirizzare
parti di un documento XML. Esso utilizza una sintassi diversa da XML per definire
espressioni che identificano particolari nodi e gruppi di nodi contenuti in un documento
XML.
Tale sintassi viene utilizzata sia da XPointer che da XSLT e da alcuni innovativi linguaggi
prposti per per l’interrogazione di basi di dati realixzzate in XML (tra i quali XQuery).
La versione 1.0 di XPath è diventata uno standard W3C il 16 novembre 1999.
Esso consente di creare espressioni dichiarative, chiamate espressioni XPath o pattern,
che individuano i vari nodi dell'albero di rappresentazione di un documento XML.
XPath opera sull’astratto, sulle strutture logiche di un documento XML, piuttosto che
riproporne la sintassi e prende il nome dall’uso di una notazione a Path come gli URLs per
la navigazione attraverso la struttura gerarchica di un documento XML. La sua sintassi è
molto compatta e, per certi versi, ricorda un po' le espressioni per individuare il percorso di
un file o una cartella su un file system.
In aggiunta all’uso della sintassi a path, XPath è anche progettato per avere un
sottoinsieme naturale che permetta di poter effettuare dei confronti (capire se un nodo ha
o non una determinata struttura).
XPath modella un documento XML come un albero di nodi (Tree Nodes). Questi nodi sono
di tipo differente, includono nodi di tipo Element, nodi di tipo Attribute e nodi di tipo Text.
XPath definisce un percorso per calcolare uno string-value per ogni tipo di nodo. Molti tipi
di nodi possono avere un nome ed è per questo che XPath ingloba completamente tutto il
Namespace di XML (lo spazio dei nomi – il namespace – XML rappresenta un metodo
semplice per la qualificazione dei nomi degli elementi e attributi utilizzati nei documenti
XML, associandoli con dei spazi dei nomi identificati con indirizzi URI).
Pertanto, il nome di un nodo è individuato come una coppia formata da una parte locale e
un possibile namespaces URI non definito; questo metodo è chiamato Expanded-name.
- 21 -
1.3.2 Modello dati XPATH
Il modello dati XPath tratta ogni documento XML come se fosse un albero composto da
nodi. Ogni nodo può essere uno dei seguenti sette tipi:
• radice (root)
Ogni documento possiede esattamente un solo nodo radice (root) che, come
indicato dal nome stesso, rappresenta la radice dell’albero dei nodi. Un nodo radice
non contiene alcuna rappresentazione della dichiarazione XML o della
dichiarazione del tipo di documento; non ha un nodo padre e il suo valore coincide
con il valore dell’elemento document;
• elemento (element)
Un nodo element rappresenta un elemento dotato di nome, URI di namespace, un
nodo padre ed una lista di nodi figlio, la quale può contenere ulteriori nodi element,
nodi commento, nodi istruzione e nodi testo. Un elemento è anche dotato di una
serie di attributi e un elenco di namespaces di riferimento, ma nessuno di tali oggetti
è trattato come nodo figlio del nodo element in esame. Il valore di un nodo element
coincide con il testo completo sottoposto a parsing che si trova tra il tag iniziale e
finale dell’elemento, dopo che tutti i tag, le istruzioni di elaborazione ed i commenti
sono stati rimossi.
• attributo (attribute)
Un nodo attributo è dotato di un nome, un URI di namespace, un valore ed un
elemento padre. Bisogna sottolineare che, nonostante gli elementi siano nodi padre
degli attributi, gli attributi non sono considerati nodi figli dei nodi element. Il valore
del nodo attributo corrisponde al valore dell’attributo normalizzato.
• testo(text)
Ogni nodo testo rappresenta il più lungo blocco contiguo di testo individuabile tra
due tag, istruzioni di elaborazione o commenti. Un nodo di testo ha un nodo padre
ma non può avere nodi figlio ed il suo valore corrisponde al testo del nodo stesso.
- 22 -
• namespace
Un nodo di namespace rappresenta un namespace che abbia influenza su un
elemento o un attributo. Come avviene anche per i nodi attributo, ogni nodo di
namespace ha un elemento padre ma non è realmente figlio di tale elemento.
• istruzione di elaborazione (processing instruction)
Un nodo istruzione di elaborazione rappresenta un’istruzione dotata di un target di
dati sui quali operare, esso è dotato di un nodo padre ma non ha nodi figli.
• commento (comment)
Un nodo commento rappresenta un commento e, come nel caso precedente, è
dotato di un nodo padre ma non ha nodi figli.
La dichiarazione XML e la dichiarazione del tipo di documento non sono incluse nella
rappresentazione utilizzata da XPath per il documento XML, tutte le referenze a entità e le
sezioni CDATA vengono risolte prima di costruire l’albero XPath. Le referenze stesse,
quindi, non vengono incluse in specifici nodi dell’albero.
Ogni espressione XPath può assumere un valore che appartiene ai seguenti quattro tipi
fondamentali:
• boolean (booleano)
Un valore binario che può assumere solamente i valori true o false. Tali valori
vengono solitamente prodotti utilizzando operatori di confronto. XPath non offre
delle espressioni letterali per i due possibili valori di un booleano. Ciononostante, al
loro posto è possibile servirsi delle due funzioni true() e false();
• number (numero)
Tutti i numeri in XPath rispecchiano lo standard IEEE 754, per questo non sono
numeri floating-point rappresentati a 64 bit. I numeri includono i valori speciali Inf
(infinito), –Inf (infinito negativo) e NaN (not a number), utilizzati per rappresentare il
risultato di operazioni illegali, come la divisione per zero. Gli operatori per i numeri
sono i canonici operatori matematici: + (addizione), - (sottrazione), *
(moltiplicazione), div (divisione), mod (resto).
- 23 -
• string (stringa)
Una stringa XPath rappresenta una sequenza di zero o più caratteri unicode. Le
stringhe letterali possono essere racchiuse tra apici sia singoli sia doppi, a seconda
delle esigenze. La concatenazione di stringhe usando il simbolo “+” non è
consentita, è necessario adoperare la funzione concat();
• node set (insieme di nodi)
Un insieme di nodi rappresenta una collezione di zero o più nodi di un documento
XML. I location path producono per la maggior parte insiemi di nodi.
La costruzione sintattica primaria in XPath è l’espressione (o path), questa viene valutata
per produrre un oggetto, che appartiene ad uno dei precedenti quattro tipi base:
Un’espressione viene valutata in base al contesto in cui si presenta. I contesti derivanti da
una espressione XPath possono essere:
• Un nodo (il context node)
• Una coppia di interi positivi diversi da zero (la context position e la context size)
• Un insieme di variabili vincolate
• Una libreria di funzioni
• Un insieme di dichiarazioni di namespace come input dell’espressione.
La context position è sempre minore o uguale alla context size.
Le variabili obbligatorie consistono in un insieme che ha come valori dai nomi di variabili ai
valori delle variabili. Il valore di una variabile è a sua volta un oggetto di un tipo specificato
precedentemente, oppure di un tipo supplementare.
La libreria di funzioni consiste in un insieme che va dai nomi delle funzioni alle funzioni
stesse. Ogni funzioni ha in input zero o più argomenti e restituisce sempre un singolo
risultato. Per una funzione contenuta nel Core di XPath vi si hanno sempre risultati nei
quattro tipi semplici.
I namespace consistono in un insieme di prefissi e di namespace URIs.
Le variabili obbligatorie, i namespace e le librerie di funzioni usati per valutare una sotto-
espressione sono sempre gli stessi che vengono usati per valutare ciò che contiene una
- 24 -
espressione. La context position, la context size e il context node sono invece a volte
differenti quando si valuta ciò che contiene una espressione.
Molti tipi di espressioni cambiano il context node mentre solo i predicati cambiano la
context size e la context position.
Le espressioni XPath sono scritte come una sequenza di passi (steps) per giungere da un
nodo XML (il corrente “context node”) ad un altro nodo o ad un insieme di nodi.
Chiameremo da ora in poi le espressioni XPath col nome di XPath query.
La query ha la forma di un percorso che si può formare sull'albero e quindi composta di
nodi separati dal carattere “/” (carattere separatore che indica il percorso).
Ogni punto ha tre componenti:
Axis Specifier
Node Test
Predicate
In XPaths sono definite due notazioni, una, è l'abbreviated syntax , che essendo molto
compatta permette che XPaths sia scritto e letto in modo molto intuitivo e, in molti casi,
sono usati caratteri e costrutti familiari. La seconda si chiama full syntax ed è molto più
pesante, ma permette di specificare molte più opzioni ed è molto descrittiva se letta
attentamente.
- 25 -
1.3.3 Sintassi di XPATH
1.3.3.1 Abbreviated e Full syntax
Abbreviated syntax
La notazione compatta permette di associare ai casi comuni molte abbreviazioni e
opzioni predefinite. L'XPath più semplice si presenta nella forma:
/A/B/C
Questa query seleziona gli elementi C che sono figli degli elementi B che a loro
volta sono figli.
XPath è una sintassi designata ad essere simile alla sintassi delle URI (Uniform
Resource Identifier) e alla sintassi dei Path dei file.
Le espressioni più complesse possono essere costruite specificando degli assi oltre
a quello del child axis che è quello predefinito. E’ possibile difatti inserire un node
test, o un semplice nome, o un attributo, che può essere scritto tra parentesi quadre
nella parte finale dell’espressione (dopo un intero passo).
Per esempio l'espressione:
A//B/*[1]
seleziona l'elemento ('[1]'), con qualsiasi nome ('*'),che è figlio('/') di un elemento B il
quale a sua volta è un figlio o altro, discendente più profondo ('//') di un elemento A
che è un figlio del contesto corrente (in quanto l'espressione non inizia con '/').
- 26 -
Full (Expanded) syntax
Nella full syntax, ovvero sintassi non abbreviata, i due esempi precedenti sarebbero
stat scritti nel modo seguente:
/child::A/child::B/child::C
child::A/descendant-or-self::node()/child::B/child::*[1]
Qui, ad ogni passo dell’espressione XPath, gli assi (axis) (child or descendant-or-
self) sono stati specificati in modo esplicito, seguiti da due coppie di punti (::) seguiti
dai nomi dei nodi.
1.3.3.2 Axis
L'Axis indica il senso di percorrenza dell'albero del documento XML. Gli Axis disponibili,
nella sintassi completa ed abbreviata, sono:
child
default, non specificato nella sintassi abbreviata
attribute
@
descendant
non disponibile nella sintassi abbreviata
descendant-or-self
//
parent
..
ancestor
non disponibile nella sintassi abbreviata
- 27 -
ancestor-or-self
non disponibile nella sintassi abbreviata
following
non disponibile nella sintassi abbreviata
preceding
non disponibile nella sintassi abbreviata
following-sibling
non disponibile nella sintassi abbreviata
preceding-sibling
non disponibile nella sintassi abbreviata
self
. (punto)
namespace
non disponibile nella sintassi abbreviata
Ad esempio, usando la seguente sintassi abbreviata,
//a/@href
si seleziona un attributo denominato href in un qualunque elemento dell'albero del
documento. L'Axis self è più comunemente usato per riferirsi al nodo attualmente
selezionato.
Per esempio l’espressione
h3[.='See also']
seleziona un elemento denominato h3 nel contesto corrente, il cui testo è uguale a "See
also".
- 28 -
1.3.3.3. Test Node
I test node possono controllare nomi specifici di nodi o espressioni più generali. Nel caso
di un documento XML in cui il prefisso del namespace gs è stato definito, l’espressione
//gs:enquiry
troverà tutti i nodi enquiry di quel namespace.
Altri node test sono:
comment()
trova un nodo di commento XML, per esempio <!-- Commento -->
text()
trova un nodo di tipo testo, per esempio hello in <k>hello</k>
processing-instruction()
trova istruzioni di processamento XML come <?php echo $a;?>. In questo caso ('php').
node()
trova il nodo.
- 29 -
1.3.3.4 Predicates
Espressioni di qualunque entità possono essere indicate fra parentesi quadre, le quali
dovranno essere soddisfatte affinché il nodo possa essere preso in considerazione.
Ad esempio l’espressione
//a[@href='help.php'
la quale abbinerà un elemento con un attributo href di cui il valore è help.php.
Non c'è limite al numero di predicati ed essi non devono essere limitati all'ultimo elemento
di un'espressione XPath ma possono anche essere annidati.
I percorsi specificati nei predicati faranno riferimento al contesto del relativo punto (cioè
quello immediatamente prima del test del nodo).
L’espressione seguente
//a[@href='help.php'][../div/@class='header']/@target
selezionerà il valore dell'attributo target di un elemento a, se l'elemento ha un attributo
href il cui valore è help.php e se il parent è un elemento div che ha un attributo class di
valore header.
- 30 -
1.3.4 Funzioni ed espressioni XPATH
La function library di Xpath include:
Funzioni per manipolare stringhe: concat(), substring(), contains(), substring-before(),
substring-after(), translate(), normalize-space(), string-length()
Funzioni per manipolare numeri: sum(), round(), floor(), ceiling()
Funzioni per accedere alle proprietà dei nodi: name(), local-name(), namespace-uri()
Funzioni per accedere a informazioni del contesto del nodo: position(), last()
Funzioni per la conversione dei tipi: string(), number(), boolean()
• Funzioni su i nodi
position()
restituisce un numero che rappresenta la posizione di questo nodo rispetto ai
relativi fratelli.
count(node-set)
restituisce il numero di nodi che corrispondono alla relativa richiesta
• Funzioni per la manipolazione di stringhe
string(object?)
converte ognuno dei quattro tipi di dati di XPath in una stringa. L'argomento può
essere un XPath, nel qual caso i nodi abbinati sono convertiti in stringa.
concat(string, string, string*)
concatena tutte le stringhe.
contains(s1, s2)
restituisce true se s1 contiene s2.
- 31 -
normalize-space(string?)
tutti i whitespace all'inizio e alla fine vengono rimossi e tutte le sequenze di
whitespace sono sostituite da un singolo spazio. È molto utile quando l'XML
originale dev'essere ben formattato, in grado di rendere ulteriormente fidata
elaborazione della stringa.
• Funzioni booleane
not(boolean)
nega tutta l'espressione booleana.
• Funzioni numeriche
sum(node-set)
Converte i valori della stringa di tutti i nodi trovati, in numeri, quindi estituisce la
somma di questi numeri.
Le espressioni possono essere generate usando gli operatori: =, !=, <=, <, >= e >. Le
espressioni booleane possono essere unite con le parentesi () e combinate con operatori
booleani and, or and not(). I calcoli numerici possono usare *, +, -, div and mod. Le
stringhe possono consistere di tutti i caratteri Unicode.
All'interno o all'esterno dei predicati, interi node-set possono essere uniti usando il
carattere | ("pipe").
Il costrutto v[x or y] | w[z] restituirà un singolo node-set con tutti gli elementi di v che
hanno come figli elementi y o x, insieme a tutti gli elementi di w che hanno figli z, trovati
nel contesto corrente. L’espressione seguente
//item[@price > 2*@discount]
seleziona gli item di cui l'attributo price è maggiore del doppio del valore dell'attributo
discount.
- 32 -
1.3.5 Esempi pratici
Analizziamo ora un semplice documento XML per capire meglio come accedere ai dati in
esso contenuti.
<?xml version="1.0"?><?xml-stylesheet type="text/xsl" href="myfile.xsl" ?> <bookstore specialty="novel"> <book style="autobiography"> <author> <first-name>Joe</first-name> <last-name>Bob</last-name> <award>Trenton Literary Review Honorable Mention</award> </author> <price>12</price> </book> <book style="textbook"> <author> <first-name>Mary</first-name> <last-name>Bob</last-name> <publication>Selected Short Stories of <first-name>Mary</first-name> <last-name>Bob</last-name> </publication> </author> <editor> <first-name>Britney</first-name> <last-name>Bob</last-name> </editor> <price>55</price> </book> <magazine style="glossy" frequency="monthly"> <price>2.50</price> <subscription price="24" per="year"/> </magazine> <book style="novel" id="myfave"> <author> <first-name>Toni</first-name> <last-name>Bob</last-name> <degree from="Trenton U">B.A.</degree> <degree from="Harvard">Ph.D.</degree> <award>Pulitzer</award> <publication>Still in Trenton</publication> <publication>Trenton Forever</publication> </author> <price intl="Canada" exchange="0.7">6.50</price> <excerpt> <p>It was a dark and stormy night.</p> <p>But then all nights in Trenton seem dark and stormy to someone who has gone through what <emph>I</emph> have.</p> <definition-list> <term>Trenton</term> <definition>misery</definition> </definition-list> </excerpt> </book> <my:book xmlns:my="uri:mynamespace" style="leather" price="29.50"> <my:title>Who's Who in Trenton</my:title> <my:author>Robert Bob</my:author></my:book></bookstore>
- 33 -
La seguente tabella mostra alcuni esempi di espressioni XPath operate sul documento
precedente.
Espressione Si riferisce a ./author Tutti gli elementi <author> nel contesto corrente. Notare
che è equivalente all'espressione nella riga successiva.
author Tutti gli elementi <author> nel contesto corrente.
first.name Tutti gli elementi <first.name> nel contesto corrente.
/bookstore L'elemento documento (<bookstore>) di questo documento.
//author Tutti gli elementi <author> nel documento.
book[/bookstore/@specialty = @style]
Tutti gli elementi <book> per cui il valore dell'attributo style è uguale al valore dell'attributo specialty dell'elemento <bookstore> al livello principale del documento.
author/first-name Tutti gli elementi <first-name> che sono elementi figlio di un elemento <author>.
bookstore//title Tutti gli elementi <title> presenti a diversi livelli all'interno dell'elemento <bookstore> (discendenti arbitrari). Notare che differisce dall'espressione nella riga successiva.
bookstore/*/title Tutti gli elementi <title> che sono elementi nipote degli elementi <bookstore>.
bookstore//book/excerpt//emph Tutti gli elementi <emph> all'interno degli elementi figlio <excerpt> degli elementi <book>, all'interno dell'elemento <bookstore>.
.//title Tutti gli elementi <title> presenti a diversi livelli nel contesto corrente. Questa situazione è essenzialmente l'unica in cui è richiesto il punto.
author/* Tutti gli elementi che sono elementi figlio di elementi <author>.
book/*/last-name Tutti gli elementi <last-name> che sono elementi nipote degli elementi <book>.
*/* Tutti gli elementi nipote del contesto corrente.
*[@specialty] Tutti gli elementi con l'attributo specialty.
@style L'attributo style nel contesto corrente.
price/@exchange L'attributo exchange sugli elementi <price> all'interno del contesto corrente.
price/@exchange/total Restituisce un set di nodi vuoto poiché gli attributi non contengono elementi figlio. Questa espressione è consentita dalla grammatica di XPath (XML Path Language), ma non è del tutto valida.
- 34 -
Espressione Si riferisce a book[@style] Tutti gli elementi <book> con attributi style nel contesto
corrente.
book/@style L'attributo style per tutti gli elementi <book> nel contesto corrente.
@* Tutti gli attributi del contesto dell'elemento corrente.
./first-name Tutti gli elementi <first-name> nel nodo di contesto corrente. Notare che è equivalente all'espressione nella riga successiva.
first-name Tutti gli elementi <first-name> nel nodo di contesto corrente.
author[1] Il primo elemento <author> nel nodo di contesto corrente.
author[first-name][3] Il terzo elemento <author> che dispone di un elemento figlio <first-name>.
my:book L'elemento <book> dallo spazio dei nomi my.
my:* Tutti gli elementi dallo spazio dei nomi my.
@my:* Tutti gli attributi dallo spazio dei nomi my (questo non include gli attributi non qualificati su elementi dallo spazio dei nomi my).
- 35 -
1.4 XQUERY 1.4.1 Origini e scopi di XQUERY XQUERY è un linguaggio ideato per il recupero delle informazioni memorizzate all'interno
di file XML. Esso è molto importante per la diffusione e l'utilizzo di XML nell'ambito di
documenti contenenti grandi quantità di dati, difatti, risulta particolarmente conveniente
avere a disposizione uno strumento relativamente facile e potente per poter recuperare
l'informazione presente in un file XML. XQuery deve permettere di realizzare delle query
(interrogazioni) sul documento proprio come avviene ad esempio con il linguaggio SQL nel
caso dei database relazionali.
Il suo nome deriva dall’acronimo XML Query Language e nasce proprio con l'intento di
realizzare un linguaggio per recuperare agevolmente le informazioni da un documento
XML ed andare a costituire una sorta di "SQL per XML". XQuery non è un linguaggio
basato su XML ed è costituito da una sintassi semplice e facilmente leggibile per
formulare, nel modo più agevole possibile, le query sui dati. Il working group del W3C ha
sviluppato anche una versione di XQuery con sintassi XML, chiamata XQueryX i cui
dettagli però esulano dagli scopi di questa tesi.
Come avremo modo di vedere, XQuery ha alcune affinità con altre tecnologie della
famiglia XML. In particolare questo linguaggio utilizza molto della sintassi di XPath.
Entrambi condividono lo stesso modello di dati, cioè lavorano sulla struttura logica ad
albero dei documenti XML. Possiamo affermare che XQuery 1.0 è un'estensione di XPath
2.0 e che ogni espressione di XPath è anche un'espressione XQuery. D’altronde, come
accade anche per XPath, in XQuery tutto è espressione, cioè qualsiasi combinazione
sintattica valida viene valutata e restituisce un valore. Rispetto a XPath, però, XQuery
mette a disposizione nuove caratteristiche che gli consentono di restituire risultati in modo
sofisticato, facendolo somigliare per certi versi a XSLT (almeno dal punto di vista delle
funzionalità offerte).
- 36 -
1.4.2 Interrogazione di documenti XML
Abbiamo detto che XQuery condivide lo stesso modello di dati e la stessa sintassi di
XPath. Pertanto, se si conosce già XPath, non si ha difficoltà a manipolare nodi ed
elementi ed a navigare nell'albero di rappresentazione di un documento XML, districandosi
tra antenati (ancestor) e discendenti (descendant), fratelli (sibling) e genitori (parent). Di
seguito riportiamo un semplice esempio; Dato il seguente documento (rappresentazione
ad albero di un documento XML):
<?xml version="1.0" ?> <magazzino codice="MAG1"> <articolo codice="A1"> <descrizione>Righello 25 cm</descrizione> <quantita>50</quantita> </articolo> <articolo codice="A2"> <descrizione>Calcolatrice</descrizione> <quantita>60</quantita> </articolo> <articolo codice="A3"> <descrizione>Quaderno a quadri</descrizione> <quantita>120</quantita> </articolo> </magazzino>
l'espressione che segue
/magazzino/articolo[quantita > 100] individua tutti gli elementi <articolo> il cui sottoelemento <quantita> ha un valore maggiore
di 100.
In particolare, nel nostro caso l'espressione individua l'elemento <articolo> con codice A3:
Ecco il risultato dell'interrogazione:
<articolo codice="A3"> <descrizione>Quaderno a quadri</descrizione> <quantita>120</quantita> </articolo>
- 37 -
Per quanto detto prima, l'espressione XPath appena vista è anche un’espressione
XQuery. Supponendo che il documento XML in questione sia memorizzato in un file
magazzino.xml, possiamo completare l'espressione nel seguente modo:
document("magazzino.xml")/magazzino/articolo[quantita > 100]
La funzione document() individua la fonte dei dati rappresentati tramite XML.
1.4.3 Espressioni FLWOR Le differenze tra XQuery ed XPath sono evidenziate dall'introduzione di costrutti che
offrono una grande potenza e flessibilità e permettono di costruire le cosiddette
espressioni FLWOR, si pronuncia come "flower", ed è un acronimo che deriva dai
seguenti costrutti:
For che fornisce un meccanismo iterativo
Let che definisce un meccanismo per l'assegnamento
Where che consente di filtrare i risultati tramite condizioni booleane
Order by che offre un meccanismo di ordinamento
Return che indica l'espressione da valutare e restituire
Mostriamo con un semplice esempio come utilizzare questi costrutti. Facendo riferimento
al documento XML mostrato all'inizio del capitolo precedente, supponiamo di voler estrarre
le descrizioni di tutti gli articoli del magazzino in quantità inferiore a 100 ordinate
alfabeticamente. Possiamo ottenere questo risultato con la seguente espressione XQuery: for $x in document("magazzino.xml")/magazzino/articolo
where $x/quantita < 100
let $y := $x/descrizione
order by $y
return $y
- 38 -
Questa espressione è costituita da un ciclo for che fa uso di una variabile $x per
rappresentare ad ogni
ciclo un elemento <articolo> del nostro documento XML.
Tramite la clausola where specifichiamo il criterio di selezione degli elementi a cui siamo
interessati. Assegniamo poi tramite let la descrizione dell'articolo alla variabile $y.
Utilizziamo order by per indicare l'ordinamento ed infine con return indichiamo cosa
restituire.
Il risultato della valutazione di questa espressione sarà il seguente:
<descrizione>Calcolatrice</descrizione> <descrizione>Righello 25 cm</descrizione>
Naturalmente potremo avere espressioni più o meno complesse in base al risultato che
vogliamo ottenere.
Una caratteristica molto interessante delle espressioni XQuery consiste nella possibilità di
essere inglobate all'interno di altri documenti, anche in forma annidata. In tal caso le
espressioni devono essere racchiuse tra parentesi graffe. È possibile quindi sfruttare
XQuery non solo come linguaggio di interrogazione, ma anche per la generazione
dinamica di pagine HTML. Vediamo un esempio facendo sempre riferimento al solito
documento XML utilizzato in precedenza. Supponiamo di voler generare una pagina
HTML che elenca le descrizioni degli articoli estratti dalla precedente espressione
FLWOR.
Possiamo farlo con la seguente espressione XQuery: <html><head><title>Articoli<title></head><body>
<h1>Articoli con disponibilità inferiore a 100 unità</h1>
<ul>
{
for $x in document("magazzino.xml")/magazzino/articolo
where $x/quantita < 100
order by $x/descrizione
return <li>{data($x/descrizione)}</li>
}
</ul>
</body>
</html>
- 39 -
L'espressione è costituita da un template HTML contenente l'espressione FLWOR
all'interno delle parentesi graffe. La clausola return genera un elemento <li> contenente il
valore dell'elemento <descrizione> estratto tramite la funzione predefinita data(). Da
notare come la chiamata data() è racchiusa tra parentesi graffe dal momento che si trova
all'interno di tag HTML. Il risultato della valutazione di questa espressione è la pagina
HTML seguente:
<html> <head> <title>Articoli<title> </head> <body> <h1>Articoli con disponibilità inferiore a 100 unità</h1> <ul> <li>Calcolatrice</li> <li>Righello 25 cm</li> </ul> </body> </html>
Non è difficile immaginare le possibili applicazioni di questa caratteristica di XQuery con le
tecnologie di elaborazione server-side come ASP, PHP, JSP, ecc.
Abbiamo visto come questo linguaggio estenda XPath offrendo costrutti per l'estrazione
avanzata di dati rappresentati con XML fino ad offrire funzionalità di generazione dinamica
di pagine HTML.
In definitiva quindi, anche se questo linguaggio si presenta come una tecnologia per
l'interrogazione di dati rappresentati in XML, abbiamo visto come si presti bene anche per
la generazione dinamica di pagine HTML (e potenzialmente anche di altri tipi di
documenti) ed abbiamo avuto modo di osservarne i punti di forza e le caratteristiche
fondamentali.
Nel paragrafo successivo vedremo come XQuery presenta anche funzionalità abbastanza
vicine ai linguaggi di programmazione.
- 40 -
1.4.4 Espressioni condizionali Una delle funzionalità di XQuery è il costrutto if-then-else per la costruzione di espressioni
condizionali. A differenza della maggior parte dei linguaggi di programmazione, la clausola
else è obbligatoria in XQuery. Questo perchè tutto in XQuery è un'espressione e pertanto
qualsiasi costrutto deve restituire un valore. Per vedere all'opera il costrutto if-then-else,
facciamo riferimento al solito documento XML mostrato nel paragrafo precedente, che per
comodità riportiamo:
<?xml version="1.0" ?> <magazzino codice="MAG1"> <articolo codice="A1"> <descrizione>Righello 25 cm</descrizione> <quantita>50</quantita> </articolo> <articolo codice="A2"> <descrizione>Calcolatrice</descrizione> <quantita>60</quantita> </articolo> <articolo codice="A3"> <descrizione>Quaderno a quadri</descrizione> <quantita>120</quantita> </articolo> </magazzino>
La seguente espressione XQuery (interrogazione condizionale con soglie di
approvvigionamento) restituisce una sequenza di elementi <msg> con la descrizione di
ciascun articolo e la scritta "quantità sufficiente" se la quantità è superiore a 50, "quantità
insufficiente" in caso contrario.
for $x in document("magazzino.xml")/magazzino/articolo
order by $x/descrizione
return if ($x/quantita <= 50) then
<msg>{data($x/descrizione)} - quantità insufficiente</msg>
else
<msg>{data($x/descrizione)} - quantità sufficiente</msg>
- 41 -
Risultato dell'interrogazione:
<msg>Calcolatrice - quantità sufficiente</msg> <msg>Quaderno a quadri - quantità sufficiente</msg> <msg>Righello 25 cm - quantità insufficiente</msg>
Abbiamo detto che il costrutto if-then-else deve sempre prevedere la clausola else.
Nel caso in cui non abbiamo da restituire un valore specifico per "else", o meglio, il valore
restituito è nullo, possiamo specificarlo tramite l'espressione (), come nel seguente
esempio (espressione condizionale con alternativa nulla):
if ($x/quantita <= 50) then
<msg>{data($x/descrizione)} - quantità insufficiente</msg>
else
()
1.4.5 Funzioni predefinite Abbiamo già avuto modo di vedere alcune funzioni predefinite. In particolare abbiamo visto
la funzione document(), che restituisce la struttura di nodi di un documento XML, e data(),
che restituisce il valore di un nodo.
XQuery prevede un vasto insieme di funzioni predefinite, condivise con XPath, che
consentono di effettuare le operazioni più disparate: dal conteggio di elementi (count())
alla somma di valori (sum()), dalla concatenazione di stringhe (concat()) alla
trasformazione in maiuscole (upper-case()), dall'estrazione di sottostringhe (substring())
all'individuazione della posizione di un nodo (position()).
Le funzioni predefinite appartengono al namespace identificato dall'URI
http://www.w3.org/2005/02/xpath-functions (http://www.w3.org/2005/02/xpath-functions) al
quale è associato il prefisso predefinito fn.
Questo è il motivo per cui talvolta le funzioni vengono indicate con questo prefisso, ad
esempio fn:max().
Tuttavia, dal momento che fn è il prefisso predefinito del namespace non è necessario
indicarlo nelle chiamate di funzione.
- 42 -
1.4.6 Funzioni personalizzate Se le funzioni predefinite non dovessero essere sufficienti per le nostre esigenze di
elaborazione, XQuery prevede la possibilità di creare nuove funzioni, proprio come un
linguaggio di programmazione strutturata.
Se ad esempio abbiamo bisogno di una funzione per il calcolo dell'IVA di un articolo,
possiamo definirla nel seguente modo:
Dichiarazione di una funzione per il calcolo dell'IVA:
declare function local:ImportoIVA($prezzo as xs:decimal, $aliquota as xs:integer) as
xs:decimal
{
return ($prezzo * $aliquota div 100)
};
La dichiarazione di una nostra funzione prevede l'indicazione di un prefisso (local
nell'esempio) in modo che le funzioni personalizzate possano essere distinte da quelle
predefinite.
È necessario indicare il tipo di dato dei parametri ed il tipo di dato restituito dalla funzione.
A tale proposito, i tipi di dato previsti sono quelli degli XML Schema.
A questo punto possiamo invocare la nostra funzione in qualsiasi punto ne abbiamo
necessità, come mostra l'esempio seguente:
return <iva>{local:ImportoIVA(data(x$/prezzo), data(x$/aliquota))}</iva>
La possibilità di definire le proprie funzioni apre la strada ad un vero e proprio linguaggio di
programmazione che, unito alle capacità di interrogazione e di generazione di codice
HTML, genera un vero e proprio framework per la generazione dinamica di pagine Web.
- 43 -
1.4.7 Differenze tra XQUERY ed XPATH Come abbiamo detto, XPath 2.0 è un linguaggio che permette di individuare i nodi
all’interno di un documento XML. XQuery è invece un vero linguaggio di programmazione
definito dal W3C e concepito per l’interrogazione di documenti e basi di dati XML. Le
espressioni XPath, a differenza delle espressioni XQuery, non servono a identificare la
struttura di un documento, bensì a localizzarne con precisione i nodi. XPath è nato
originariamente dall’esigenza di fornire una sintassi e un comportamento comune fra
XPointer e XSL; Esso è stato successivamente adottato dagli sviluppatori come metodo di
interrogazione dei dati in formato XML. XPath 2.0 è un superset di XPath 1.0, con l’
aggiunta di nuovi tipi di dati e con la possibilità di sfruttare le informazioni derivanti
dall’utilizzo di uno schema XML per la convalida dei documenti. XQuery a sua volta
condivide numerose espressioni con XPath 2.0 prendendo spunto da SQL, ed utilizza i
documenti XML come sogenti di dati allo stesso modo in cui SQL utilizza i database.
Mentre i dati memorizzati nelle basi di dati relazionali tendono ad assumere una forma
"piatta" oppure tabellare, i dati salvati con XML sono, per definizione, irregolari e nidificati
e possono contenere elementi ricorsivi. L’utilizzo di XQuery dovrebbe quindi rivelarsi
radicalmente più semplice e probabilmente in alcuni casi più performante per
l’interrogazione di strutture dati realizzate con lo standard XML rispetto agli altri linguaggi.
- 44 -
Capitolo
2 Aspetti progettuali del software
- 45 -
2.1 Analisi del problema
Le finalità del progetto puntano a fornire un valido aiuto a coloro che devono creare
documenti in XML vincolati da una struttura espressa in una DTD e fornire uno strumento
capace effettuare un analisi statistica su tali documenti XML generati. Il software deve
essere di facile utilizzo per l’utente finale e fornire un comodo strumento per la valutazione
dei risultati generati.
2.2 Motivazioni
Le motivazioni che ci hanno spinto alla creazione del software “Recursive XML Generator”
vanno ricercate nell’esigenza di fornire agli utenti un modo semplice per ottenere
documenti XML, composti anche da molti nodi, che possano essere utilizzati soprattutto
come oggetti sui quali operare query tramite motori XQuery/XPath per studiarne il
comportamento e le prestazioni. L’inserimento di ricorsioni in maniera controllata è stato
previsto al fine di effettuare query complesse specificamente con XQuery poichè esse non
sono realizzabili con altri linguaggi come XPath.
2.3 Progettazione Astratta
La progettazione del software ci ha permesso di stilare ad un elevato livello di astrazione il
seguente schema UML:
Main Form
ShowFile
Functions Dtd2Xsd
About
Dett
- 46 -
MainForm: Classe principale dell’applicazione; fornisce l’interfaccia grafica e tutti i metodi
utili per la generazione del documento XML.
ShowFile: Classe dell’applicazione che fornisce l’interfaccia grafica e gli strumenti per la
visualizzazione di file di testo.
About: Classe dell’applicazione che fornisce l’interfaccia grafica per le info sul
programma.
Dett: Classe dell’applicazione che fornisce l’interfaccia grafica per visualizzare, caricare,
salvare e confrontare i dettagli del file XML generato.
Dtd2Xsd, Functions: Classi che forniscono funzioni di utilità per effettuare il parsing e la
validazione delle DTD.
2.4 Scelta del Linguaggio
2.4.1 Il linguaggio C#
Il C# (si pronuncia C sharp, sharp in inglese significa "in gamba" o più semplicemente
diesis) è un linguaggio di programmazione object-oriented sviluppato da Microsoft
all'interno dell'iniziativa .NET, e successivamente approvato come standard ECMA. Il C# si
ispira al C++ ed a Java per quanto riguarda la sintassi, ed a Visual Basic per gli strumenti
di programmazione visuale, ed è stato sviluppato ponendo una particolare attenzione alla
semplicità.
C# è, in un certo senso, il linguaggio che meglio degli altri descrive le linee guida sulle
quali ogni programma .NET gira; questo linguaggio è stato infatti creato da Microsoft
specificamente per la programmazione nel Framework .NET. I suoi tipi di dati "primitivi"
hanno una corrispondenza univoca con i tipi .NET e molte delle sue astrazioni, come
classi, interfacce, delegati ed eccezioni, espongono esplicitamente caratterisitche proprie
del .NET framework.
- 47 -
2.4.2 Differenze con altri linguaggi
In confronto al C od al C++ il linguaggio ha subito una serie di modifiche volte
principalmente ad evitare quegli errori tipici della programmazione C:
• I puntatori possono essere utilizzati solo in particolari blocchi di codice marcati
come "unsafe".
• In molte operazioni aritmetiche vengono controllati eventuali "overflow".
• Gli oggetti dinamici non vengono deallocati esplicitamente; la loro rimozione viene
gestita automaticamente (implicitamente) dal "garbage-collector" quando non
esistono più riferimenti a tali oggetti. Questa gestione evita i due problemi ben noti
dei dangling pointer e del memory leak.
• Come in Java, è possibile ereditare da una sola classe (diversamente da come
avviene in C++) ma è possibile implementare un numero indefinito di interfacce.
• Le sole conversioni implicite che sono consentite sono quelle "safe", ovvero che
non espongono al rischio di perdita di dati causata dalla diversa tipologia di dato.
Ad esempio non sono consentite conversioni implicite fra integer e boolean o fra
enumerati ed integer.
• C# non possiede i "templates" (tipici del C++) ma nella versione 2.0 sono stati
introdotti i "generics".
Sebbene C# sia ritenuto simile a Java, esistono alcune importanti differenze fra i due
linguaggi:
• Java non gestisce le proprietà di una classe.
• Java non permette il sovraccarico (overload) degli operatori.
• Java non permette blocchi di codice "unsafe" che consentono di gestire i puntatori.
• Java utilizza i commenti Javadoc-sintax per generare la documentazione dal codice
sorgente, mentre C# utilizza la sintassi XML nei commenti per lo stesso scopo.
• C# supporta gli indicizzatori ed i delegati.
• C# supporta le strutture; blocchi di memoria che non derivano dalla classe object e
che sono memorizzati nello stack (value-type).
- 48 -
Come Java ha i suoi package anche nel C# possiamo ritrovare una serie di classi già
sviluppate per l'interazione con i vari ambienti, Front End, Database, Xml e altri. Questo è
il .NET framework, del quale utilizza una serie di librerie di classi che gli permettono
l'accesso alle funzionalità del sistema. In C# quello che in Java è chiamato package viene
chiamato namespace o "spazi di nomi". Le classi sono organizzate all'interno di una serie
di namespaces che raggruppano le classi con funzionalità simili; ad esempio
System.Windows.Forms per la gestione delle finestre di tipo "Forms", System.Xml per
l'elaborazione di XML e System.Data per l'accesso alle basi dati.
Un ulteriore livello di organizzazione è costituito dagli "assembly". Un assembly può
essere un singolo file od una serie di files linkati fra di loro. Un assembly può avere al suo
interno diversi spazi di nomi.
2.4.3 Esempi di utilizzo del C# Cominciamo con il più classico degli esempi: il seguente esempio stampa il testo "Hello
World".
Hello, world! Il seguente esempio stampa il testo "Hello World". using System; class HelloWorldApp { public static void Main() { Console.WriteLine("Hello World"); } }
Il seguente esempio definisce una classe di nome persona che espone una proprietà
nome il cui valore viene memorizzato nella variabile m_nome:
- 49 -
using System; class ClasseUno { private string m_nome; public property Nome() { get { return m_nome; } set(string value} { m_nome = value; } } }
Da notare che il linguaggio C# risulta essere potente strumento per l'utilizzo delle librerie
presenti nei namespace del Framework.NET di Microsoft. A parte le strutture interne al
linguaggio, il programmatore C# deve fare riferimento alle librerie, anche per la gestione
dell'I/O, come del resto in C e C++. Nell'esempio presentato infatti viene utilizzata la
direttiva using per l'implementazione del namespace System dal quale sarà poi utilizzata il
metodo WriteLine della classe Console per mandare in output a video la scritta "Hello
World".
2.4.4 Standardizzazione
Microsoft ha sottoposto C# ad ECMA per una standardizzazione formale. Nel dicembre del
2001 ECMA ha rilasciato "ECMA-334 C# Language Specification". C# è diventato uno
standard ISO nel 2003 (ISO/IEC 23270).
Esistono implementazioni indipendenti di .NET e del C#, fra cui il progetto Mono di Ximian
e dotGNU & Portable.NET della Free Software Foundation.
Recentemente Microsoft ha introdotto, nella versione 2.0 del framework, i "generics", le
"partial-class" ed altre nuove funzionalità. È stata proposta la standardizzazione
ECMA/ISO anche per queste funzionalità, ma allo stato attuale non fanno parte della
definizione standard del linguaggio.
- 50 -
2.5 Struttura logica dell’applicazione Il software XML Recursive Generator contiene al proprio interno un complesso motore1 di
generazione pseudo-casuale di nodi dell’albero XML. Lo scopo di questo paragrafo è
quello di cercare di fornire un supporto alla comprensione della logica adoperata da tale
motore di generazione per realizzare alberi di disparate tipologie. Il motore di generazione
è composto principalmente da quattro funzioni:
• Una funzione che percorre i rami dell’albero per decidere il possibile punto di
inserimento di un nuovo nodo
• Una funzione che decide quale tipologia di nodo inserire in tale punto per rispettare
i vincoli parametrici (ricorsioni dirette, indirette, molteplicità ecc...)
• Una funzione di parsing della DTD che funge da controllore allo scopo di verificare
che il documento generato rispetti i vincoli della DTD stessa.
• A queste funzioni ovviamente è affiancato un generatore di numeri casuali che
genera valori interi in un intervallo definito (passato in argomento alla funzione di
generazione).
Il motore genera inizialmente un albero vuoto ponendo come unico nodo (radice) il nodo
root estrapolato tramite il parsing della DTD che l’utente ha scelto come schema del file da
generare. In questa fase in pratica viene creato un documento XML composto unicamente
dal nodo root. Successivamente a questo passaggio, viene invocata la funzione per
l’aggiunta pseudo-casuale di nodi all’albero tante volte quanti sono i nodi da aggiungere.
Questa strategia di aggiungere nodi all’albero in maniera sequenziale è stata adoperata
allo scopo di mantenere il grado di complessità spaziale quanto più lineare possibile, in
modo da poter generare alberi con numerosi nodi anche su computer con risorse di
calcolo e di memoria non eccessivamente elevate. Tale compromesso va ovviamente a
1 Con tale termine vogliamo intendere un insieme di funzioni cooperanti per perseguire un comune scopo. Nel caso qui descritto tale scopo è la generazione della struttura XML.
- 51 -
discapito del tempo di esecuzione che aumenta in funzione dei nodi da generare. Per
maggiori informazioni a riguardo si rimanda al capitolo 4 “Test effettuati e comportamento
del software”. Tornando alla funzione di generazione dei nodi, descriviamone in parole
semplici la tipologia di funzionamento:
• La funzione prende in argomento il nodo radice dell’albero al quale deve
aggiungere un figlio e controlla se possiede dei nodi figli.
• Se la funzione non possiede figli salta al passo successivo, altrimenti sceglie in
maniera casuale (con una probabilità settata tramite un parametro interno) se
aggiungere un figlio al nodo corrente o se selezionare uno dei figli del nodo in
esame (in maniera sempre casuale) come candidato per essere padre del nodo da
aggiungere.
• Viene effettuato un controllo tramite la funzione di parsing della DTD per verificare
se il nodo scelto può avere dei figli e di quale tipo e caratteristiche essi devono
essere.
• Viene effettuato un controllo tramite un’altra funzione sui parametri inseriti
dall’utente (altezza, larghezza, ricorsioni, ecc...) per verificare la possibilità di
inserire il nodo in quel dato punto dell’albero.
• Se i controlli descritti nei due passi precedenti danno esito positivo il nodo, viene
inserito, altrimenti la funzione torna su di un livello e tenta una nuova strada (magari
aggiungendo un fratello al nodo in esame o scendendo in un altro nodo...)
- 52 -
Il grafico seguente illustra per sommi capi la strategia utilizzata dal motore di generazione
dei nodi (per comodità la funzione che aggiunge i nodi sarà denominata CreaAlberoXML):
Diagramma 1 – Funzione CreaAlbero
Dal diagramma si evince che la scelta probabilistica di maggiore importanza per la
generazione dell’albero è quella che avviene nel primo riquadro esagonale (quello posto
più in alto), in quanto condiziona la “forma” dell’albero generato.
Tale scelta infatti influisce sulla facoltà di dare “precedenza” alla generazione di nodi sul
medesimo livello (espansione orizzontale dell’albero) o privilegiare la discesa nei nodi figli.
Per impostazione predefinita il software assegna una probabilità di inserire il nodo sul
livello corrente del 10% e nel restante 90% dei casi scende (laddove possibile) in un nodo
figlio del nodo in esame (sviluppando l’albero in verticale).
Unico caso di fallimento dell’inserimento è la risalita fino al nodo radice (impossibilità di
inserire nodi o discendere nei figli) che si verifica solitmente solo quando i parametri di
generazione sono particolarmente rigidi ed il numero dei nodi è basso. L’albero generato
risulta in tale caso fortemente condizionato.
Una volta deciso il punto in cui inserire un nodo, la scelta della tipologia del nodo da
inserire è affidata alla funzione di inserimento (denominata nel diagramma, per comodità,
Inserisci()).
- 53 -
La funzione Inserisci() è spiegata più in dettaglio nel diagramma successivo:
Diagramma 2 – Funzione Inserisci
La funzione controlla se vi sono delle ricorsioni dirette o indirette da generare (in base a
valori archiviati in alcune variabili di sistema che fungono da contatori) ed opera una
scelta. Se vi sono ancora ricorsioni da generare il software effettua un sorteggio (riquadro
esagonale più in basso) tra l’inserimento o meno di un nodo che generi qualunque dei due
tipi di ricorsione (valore predefinito: 50% di possibilità di inserire o meno una ricorsione).
Bisogna precisare che il sorteggio è influenzato anche dal numero di nodi rimanenti da
aggiungere all’albero.
Se tale numero è minore o uguale al numero del totale delle ricorsioni da inserire, viene
data precedenza alle ricorsioni mancanti rimaste in lista.
In altre parole se la funzione “si accorge” che vi sono pochi nodi rimanenti per completare
l’albero cerca di soddisfare il più possibilie le richieste di inserimento di nodi ricorsivi
(basandosi sulla lista delle ricorsioni rimanenti).
Tale comportamento per semplicità non è stato illustrato nel diagramma precedente.
Se il sorteggio favorisce l’inserimento di una ricorsione, viene inserita una ricorsione
diretta o indiretta in base al numero delle ricorsioni rimanenti da inserire. In caso di
euguaglianza viene sorteggiata con probabilità del 50% una delle due, mentre
normalmente prevale quella con maggor numero di nodi da inserire.
- 54 -
Se invece il caso non favorisce l’inserimento di una ricorsione, viene sorteggiato (con
uguale distribuzione di probabilità) uno dei potenziali figli del nodo in esame che non
generi una ricorsione. La funzione è consapevole di quale figlio può generare una
ricorsione e quale invece no, esaminando una lista che contiene i nodi attraversati per
arrivare al nodo in esame stilata a run-time. Un importante caratteristica da notare,
riguardante il programma, è la capacità di conoscere, dopo aver effettuato il parsing della
DTD, quali nodi possono ospitare ricorsioni dirette o indirette. Tale risultato è ottenuto
esaminando la DTD come se si trattasse di un grafo orientato i cui vertici rappresentano i
nodi XML e gli archi le relazioni padre-figlio. In tal modo avremo ad esempio, che un nodo
il quale presenta un arco che torna sul nodo stesso può generare una ricorsione diretta.
Mentre se vi è almeno un percorso ad anello che attraversa uno o più nodi del grafo per
poi giungere al nodo di partenza, allora quel nodo potrà generare una ricorsione indiretta.
Per sapere quali nodi possono generare ricorsioni e di quale tipo, la funzione di parsing
effettua inizialmente una visita del grafo derivante dalla struttura della DTD allo scopo di
creare una lista dei nodi del grafo. Dopodichè la funzione effettua tante visite quanti sono i
nodi della lista (partendo ovviamente ogni volta da un nodo diverso) e verificando
l’esistenza di anelli che permettano di tornare al nodo di partenza. Gli anelli trovati e la loro
tipologia vengono archiviati (rappresentano le ricorsioni che possono essere generate
partendo da uno specifico nodo). Il seguente grafico di esempio rappresenta una tipica
DTD:
- 55 -
Riportiamo di seguito la DTD interessata:
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT dialog (turn+)> <!ATTLIST dialog dialog_att1 CDATA #IMPLIED dialog_att2 CDATA #IMPLIED dialog_att3 CDATA #IMPLIED > <!ELEMENT turn (sentence+)> <!ATTLIST turn turn_att1 CDATA #IMPLIED turn_att2 CDATA #IMPLIED turn_att3 CDATA #IMPLIED > <!ELEMENT sentence (sentence+|clause+)+> <!ATTLIST sentence sentence_att1 CDATA #IMPLIED sentence_att2 CDATA #IMPLIED sentence_att3 CDATA #IMPLIED > <!ELEMENT clause (sentence+ | clause+ | NP* | VP* | PP* | ADVP* | UNP*)+> <!ATTLIST clause clause_att1 CDATA #IMPLIED clause_att2 CDATA #IMPLIED clause_att3 CDATA #IMPLIED > <!ELEMENT NP (#PCDATA | sentence | clause | NP | PP | ADJP | UNP)*> <!ATTLIST NP NP_att1 CDATA #IMPLIED NP_att2 CDATA #IMPLIED NP_att3 CDATA #IMPLIED > <!ELEMENT VP (#PCDATA| sentence | clause | NP | PP | UNP)*> <!ATTLIST VP VP_att1 CDATA #IMPLIED VP_att2 CDATA #IMPLIED VP_att3 CDATA #IMPLIED > <!ELEMENT PP (#PCDATA| sentence | clause | NP | PP | UNP)*> <!ATTLIST PP PP_att1 CDATA #IMPLIED PP_att2 CDATA #IMPLIED PP_att3 CDATA #IMPLIED > <!ELEMENT ADVP (#PCDATA)> <!ATTLIST ADVP ADVP_att1 CDATA #IMPLIED ADVP_att2 CDATA #IMPLIED ADVP_att3 CDATA #IMPLIED > <!ELEMENT UNP (#PCDATA)> <!ATTLIST UNP UNP_att1 CDATA #IMPLIED UNP_att2 CDATA #IMPLIED UNP_att3 CDATA #IMPLIED > <!ELEMENT ADJP (#PCDATA)> <!ATTLIST ADJP ADJP_att1 CDATA #IMPLIED ADJP_att2 CDATA #IMPLIED ADJP_att3 CDATA #IMPLIED >
- 56 -
Nel grafico seguente abbiamo evidenziato a scopo esemplificativo i nodi che presentano
ricorsioni dirette ed in quello successivo un possibile percorso per generare una ricorsione
indiretta del nodo SENTENCE:
Dal grafico si evince che il programma rileverà potenziali ricorsioni dirette sui nodi
SENTENCE, CLAUSE, NP e PP.
Il programma inoltre rileverà potenziali ricorsioni indirette sui nodi SENTENCE, CLAUSE,
NP, PP e VP (infatti osservando attentamente il grafo il lettore noterà che per tutti quanti i
nodi precedentemente elencati esiste un percorso ciclico che riporta al nodo stesso
attraversando almeno un nodo diverso da quello di partenza).
- 57 -
Capitolo
3 Descrizione del software
- 58 -
3.1 Descrizione generale
XML recursive generator è un tool ideato e implementato allo scopo di permettere la
generazione di documenti XML in maniera pseudo-casuale, seguendo vincoli di
generazione dettati da parametri impostati dall’utente tramite un’interfaccia grafica (GUI)
Windows-Like e rispettando pedissequamente, allo stesso tempo, lo schema di
generazione dettato da una specifica DTD.
3.2 Generazione di un documento XML di esempio
All’avvio il software genera una finestra Windows tramite la quale è possibile accedere alle
principali funzionalità del programma (figura 1).
Fig.1 – Finestra principale dell’applicazione.
Per generare correttamente un documento XML il programma richiede che vengano
eseguiti una serie di passaggi che descriveremo in maniera esauriente:
- 59 -
L’utente deve innanzitutto selezionare la DTD dalla quale estrapolare lo schema per la
generazione della struttura XML tramite il tasto “Sfoglia...” situato in alto a sinistra come
illustrato (figura 2) oppure accedere alla voce “Apri DTD...” posta nel menù FILE (figura 3).
Fig.2 – Selezione della DTD. Fig.3 – Voce di menù “FILE->Apri DTD...”.
Dopo aver selezionato il file DTD da caricare, alla pressione del tasto “Apri” (figura 4) il
programma esegue un parsing della DTD selezionata per verificarne la correttezza.
Fig.4 – Finestra “seleziona DTD”.
Se la DTD risulta essere valida il nome della stessa apparirà a fianco della scritta “DTD in
esame” ed un messaggio “DTD Valida!” su sfondo verde ci darà conferma dell’esito
- 60 -
positivo del parsing. In caso contrario apparirà il messaggio “DTD Errata” su sfondo rosso
(figura 5) e verrà generato nella directory del programma un file “dtd_parse_error.log”
contenente un messaggio di errore (figura 6).
Fig.5 – Il parsing ha dato esito negativo.
Fig.6 – Esempio di errore descritto nel file di log.
- 61 -
A questo punto sarà possibile visualizzare all’interno del riquadri della finestra principale
dell’applicazione dei valori che indicano i settaggi di default dell’applicazione riferiti alla
DTD in esame (figura 7).
Fig.7 – I parametri di default vengono settati in maniera automatica.
3.3 Analisi teorica dei parametri settabili
Il software permette all’utente di settare undici parametri che influiscono in maniera diretta
sulla generazione del documento XML. Tali parametri vengono settati attraverso la finestra
principale dell’applicazione. Il motivo principale dell’inserimento di questi parametri è
quello di “pilotare” in un certo senso la generazione dell’albero XML in modo da fargli
rispettare dei vincoli ben definiti e permettere lo studio della struttura risultante al variare di
tali vincoli. I vincoli si dividono in due categorie: vincoli riguardanti le ricorsioni e vincoli
riguardanti la struttura dell’albero. Per quanto riguarda i vincoli riguardanti le ricorsioni
bisogna dire che durante la progettazione si è scelto di gestire innanzitutto la percentuale
stimata di ricorsioni da generare rispetto al totale dei nodi dell’albero, ed in seguito la
- 62 -
tipologia dei nodi sui quali operare tale ricorsione. L’utente può inoltre decidere da quale
livello in poi possono essere inserite ricorsioni e fino a quale livello di profondità esse
possono essere generate.
Per quanto riguarda la struttura dell’albero bisogna dire che per far si che l’utente potesse
controllare quasi completamente la “forma” dell’albero, oltre a permettergli di impostare il
numero totale di nodi, si è pensato di vincolare la minima e massima espansione
orizzontale e verticale dell’albero in modo da dominare completamente le sue
caratteristiche strutturali fondamentali.
3.4 Settaggio pratico dei parametri I parametri settabili sono:
• Numero massimo dei nodi da generare (fig. 8 - riquadro A)
Rappresenta il numero massimo di nodi che possono essere generati. Il
software cerca di generare un numero di nodi quanto più possibile vicino a
questo valore. Ovviamente non è sempre possibile generare un numero di
nodi che eguagli in ogni caso il valore di tale indice a causa della
strutturazione dell’albero stesso ed anche per via di eventuali vincoli posti da
altri parametri. Il numero dei nodi generati è comunque sempre minore o
uguale al valore indicato dal parametro. NOTA: modificando questo valore
vengono modificati di default anche i parametri “Livello massimo di
ricorsione” , “Massima espansione verticale” e “Massima espansione
orizzontale”, legati per ovvi motivi al valore di questo parametro.
• Percentuale stimata di ricorsione diretta (fig. 8 - riquadro B)
Indica la percentuale massima di nodi (rispetto al numero totale) che si
desidera far godere della proprietà di essere direttamente ricorsivi. Il valore
della percentuale è condizionato al numero di nodi. Se ad esempio il valore
è 30% ed il numero di nodi da generare è 10, al più 3 nodi, rispetto al totale
di quelli da generare, godranno di ricorsioni dirette. Ovviamente il valore di
questo parametro non può essere maggiore della percentuale massima di
ricorsione applicabile al documento.
- 63 -
• Percentuale stimata di ricorsione indiretta (fig. 8 - riquadro C)
Indica la percentuale massima di nodi (rispetto al numero totale) che si
desidera far godere della proprietà di essere indirettamente ricorsivi. Il valore
della percentuale è condizionato al numero di nodi. Se ad esempio il valore
è 30% ed il numero di nodi da generare è 10, al più 3 nodi, rispetto al totale
di quelli da generare, godranno di ricorsioni indirette. Ovviamente il valore di
questo parametro non può essere maggiore della percentuale massima di
ricorsione applicabile al documento.
• Nodi con ricorsione diretta (fig. 8 - riquadro D)
Questo riquadro viene riempito in maniera automatica del programma,
durante la fase di parsing della DTD, da un elenco di nodi che risultano avere
in potenza la possibilità di generare una ricorsione diretta. Ogni nodo è
affiancato da una checkbox (selezionata di default) che indica la possibilità di
quel nodo di essere sorteggiato per generare una ricorsione diretta quando
richiesto.
• Nodi con ricorsione indiretta (fig. 8 - riquadro E)
Questo riquadro viene riempito in maniera automatica del programma,
durante la fase di parsing della DTD, da un elenco di nodi che risultano avere
in potenza la possibilità di generare una ricorsione indiretta. Ogni nodo è
affiancato da una checkbox (selezionata di default) che indica la possibilità di
quel nodo di essere sorteggiato per generare una ricorsione indiretta quando
richiesto.
• Minima espansione verticale dell’albero di nodi (fig. 8 - riquadro F)
Il valore di questo parametro condiziona l’altezza dell’albero in modo tale che
il programma tenterà di generare un albero con altezza minima pari a tale
valore (se consentito nella DTD).
- 64 -
• Massima espansione verticale dell’albero di nodi (fig. 8 - riquadro G)
Il valore di questo parametro condiziona l’altezza dell’albero in modo tale che
il programma tenterà di generare un albero con altezza masima pari a tale
valore (se consentito nella DTD e permesso dai parametri di ricorsione).
• Minima espansione orizzontale dell’albero di nodi (fig. 8 - riquadro H)
Il valore del parametro rappresenta la minima espansione orizzontale
(ampiezza) che deve caratterizzare l’albero XML generato. Un valore pari a
5, ad esempio, sta ad indicare che almeno in un caso vi saranno 5 figli sullo
stesso livello dell’albero generato (se consentito nella DTD).
• Massima espansione orizzontale dell’albero di nodi (fig. 8 - riquadro I)
Il valore del parametro rappresenta la minima espansione orizzontale
(ampiezza) che deve caratterizzare l’albero XML generato. Un valore pari a
5, ad esempio, sta ad indicare che almeno in un caso vi saranno 5 figli sullo
stesso livello dell’albero generato (se consentito nella DTD).
• Livello minimo a partire dal quale possono essere inseriti nodi ricorsivi all’interno
della struttura XML (fig. 8 - riquadro L)
I nodi ricorsivi all’interno della struttura potranno essere inseriti a partire dal
livello che ha profondità pari al valore di questo parametro. Il parametro è
ovviamente condizionato all’altezza dell’albero ed al numero di nodi.
• Livello massimo fino al quale possono essere inseriti nodi ricorsivi all’interno della
struttura XML (fig. 8 - riquadro M)
I nodi ricorsivi all’interno della struttura potranno essere inseriti fino al livello
che ha profondità pari al valore di questo parametro. Il parametro è
ovviamente condizionato all’altezza dell’albero ed al numero di nodi.
- 65 -
Tutti i parametri sopracitati sono soggetti a controlli automatici da parte del programma in
fase di immissione dei valori in modo da evitare l’immissione di valori errati (figure
9,10,11,12).
La pseudo-casualità del motore di generazione dei nodi, permetterà nella maggior parte
dei casi (soprattutto se si generano un gran numero di nodi) a garantire che le strutture
XML prodotte siano quasi sempre diverse tra loro, e si noterà che alcuni dei parametri
sopracitati tendono ad essere, per tale motivazione, più o meno influenti sulle
caratteristiche della struttura generata, al variare della rigidità delle “restrizioni” imposte.
Fig.8 – Parametri settabili dall’utente.
Fig.9 – Errore: è stato impostato un livello massimo di
ricorsione maggiore della profondità dell’albero
Fig.10 – Errore: parametro “Liv. Min. Ric”non corretto
(impostato valore non numerico)
- 66 -
Fig.11 – Errore: è stato impostato un livello minimo di
ricorsione minore dell’altezza minima dell’albero.
Fig.12 – Errore: è stata impostata una percentuale di ric.
diretta maggiore della percentuale di ricorsione totale.
Dopo aver settato correttamente i parametri è ora possibile passare alla fase di
generazione del file XML tramite il pulsante “Genera file XML...”. La pressione di questo
tasto causa l’apertura di una finestra di salvataggio che richiede l’immissione di un nome
da assegnare al file che conterrà la struttura XML generata (figura 13) dopodichè il
software produrrà il documento, ed il processo di generazione potrà essere seguito
osservando la barra di avanzamento in basso a destra (figura 14). Il riquadro in basso a
sinistra invece è utile per sapere su quale file generato si sta lavorando.
Fig.13 – Finestra di salvataggio del file XML
- 67 -
Fig.14 – Indicatore di avanzamento
3.5 Valutazione dell’XML Generato
Un altro punto di forza del programma XML recursive generator è la possibilità di
visualizzare dettagliatamente e tramite grafici, i risultati delle generazioni di documenti
XML effettuate e confrontarli rapidamente. Il programma offre infatti la possibilità di
visionare rapidamente il documento XML generato (figura 15) nonchè di generare una
finestra contenente informazioni dettagliate sui nodi, le ricorsioni totali, dirette ed indirette
(generate e rimanenti) ed un grafico a barre verticali con i valori esatti della molteplicità di
ciascun tipo di nodo generato (figura 16-17).
Fig.15 – Visualizzazione rapida del documento XML generato
- 68 -
Fig.16 – Bottone che apre la finestra dettagli dell’XML generato.
Fig.17 – Finestra dettagli dell’XML generato.
- 69 -
Esaminando la finestra dettagli dell’XML generato (figura 17) si nota che è posibile salvare
facilmente i dettagli della generazione del documento XML (anche i dati di generazione
vengono salvati a loro volta sottoforma di un documento XML) per confrontarli
successivamente con i risultati di ulteriori generazioni in modo da poter effettuare analisi
statistiche dei risultati.
E’ inoltre possibile regolare il fattore di trasparenza della finestra per effettuare confronti
diretti con altri grafici (figura 18).
Fig.18 – Sovrapposizione di grafici per un confronto rapido dei valori.
- 70 -
3.6 Lo strumento XQuery resolver
Il programma XML recursive generator, dispone di un modulo per eseguire query XQuery
sui documenti generati, la finestra contenente il modulo per eseguire le query può essere
attivata dopo la generazione di un documento XML tramite l’apposito bottone (Fig.19).
Il nucleo funzionale dello strumento XQuery resolver è stato realizzato utilizzando il
software open source SAXON (www.saxonica.com), il quale dispone di uno motore per
l’esecuzione di query XPath 2.0 ed XQuery 1.0.
Fig.19 – Bottone per aprire la finestra dell’XQuery resolver
Fig.20 – Finestra dell’XQuery resolver
- 71 -
La finestra dell’XQuery resolver è composta da due form. Il form più in alto (a sfondo
azzurro) serve per l’input della query da eseguire, mentre quello posto più in basso mostra
l’output ottenuto dall’esecuzione del comando.
Tra i due form è posto il pulsante “Esegui Query” che permette di dare il via all’esecuzione.
Un esempio di utilizzo è mostrato in figura 21.
Fig.21 – Query eseguita su un documento.
3.7 Altre funzionalità
Il programma XML recursive generator, oltre alle caratteristiche già esaminate, dispone di
una funzione di visualizzazione rapida della DTD caricata dall’utente che contiene lo
- 72 -
schema che viene utilizzato per generare il documento XML. L’utente può fruire di tale
funzionalità semplicemente premendo sul pulsante “Visualizza DTD” in alto a destra:
verrà aperta una finestra con il contenuto ed il percorso del file DTD caricato (figura 22).
Fig.22 – Visualizzazione della DTD
Il programma dispone inoltre di una “finestra di stato” nella quale è possibile visualizzare
messaggi che descrivono le azioni che il programma sta eseguendo e/o i risultati delle
elaborazioni compiute nonchè gli errori eventualmente generati (Fig. 23).
Fig.23 – Finestra di stato
- 73 -
Capitolo
4 Testing e valutazioni
- 74 -
4.1 Valutazione delle prestazioni spaziali e temporali del software
Allo scopo di valutare le prestazioni spaziali e temporali del software sono stati eseguiti
una serie di test che riportiamo di seguito:
Il primo test è stato effettuato allo scopo di valutare il tempo (in secondi) impiegato dal
programma per generare alberi con e senza ricorsioni aumentando gradualmente il
numero dei nodi. Il seguente grafico (Grafico 1) riporta i risultati del test in forma di
diagramma: la linea rappresenta l’andamento del tempo impiegato per generare nodi via
via più numerosi. Riportiamo successivamente i risultati ottenuti in formato tabellare
(Tabella 1).
Nodi Generati Tempo Impiegato senza ric. (sec) Tempo Impiegato con ric. (sec) 256 0,22 0,28 512 0,43 0,33 1024 1,49 1,57 2048 2,27 2,05 4096 5,72 6,03 8192 11,13 11,69 16384 69,43 69,77 32768 275,72 277,56 65536 1132,45 1126,87 131072 4012,08 4004,87
Grafico 1/Tabella 1 – Prestazioni temporali del software
- 75 -
Dai dati ottenuti si può notare che il tempo impiegato per generare l’albero non varia
sensibilmente inserendo od omettendo ricorsioni durante la creazione dello stesso.
L’andamento della curva temporale è di tipo lineare in entrambi i casi ed aumenta con
l’aumentare del numero di nodi. I tempi di generazione si sono dimostrati accettabili anche
per alberi piuttosto grandi considerando che il computer utilizzato è un PC di fascia
comune e non un costoso server di calcolo (NOTA: la DTD utilizzata come modello per il
test è riportata nel paragrafo “Struttura logica dell’applicazione” del capitolo 2). Riportiamo
di seguito un grafico (Grafico 2) che illustra l’utilizzo del processore durante la generazione
dell’albero:
Grafico 2 - Esempio di grafico di utilizzo tipico del processore nell’arco di 5 minuti
(durante la generazione di un albero XML con 40000 nodi in configurazione di default).
Esaminando il grafico precedente si può notare un impiego elevato e perlopiù costante del
processore durante il processo di elaborazione, sintomo di una buona ripartizione delle
risorse di calcolo. La linea dell’impiego del processore effettua una brusca salita fino al
livello di regime per poi mantenersi prevalentemente su tale livello fino alla fine
dell’esecuzione tranne per il manifestarsi di piccoli picchi negativi dovuti alla risalita in
qualche ramo della struttura.
4.2 Valutazione dell’output generato
Per effettuare delle valutazioni sull’output generato abbiamo effettuato una serie di
generazioni di alberi XML variando i parametri di generazione e studiando gli effetti che tali
- 76 -
variazioni hanno avuto sulla struttura degli alberi XML generati (NOTA: la DTD utilizzata
come modello per i test è riportata nel paragrafo “Struttura logica dell’applicazione” del
capitolo 2).
Per la valutazione delle ricorsioni presentiamo di seguito i seguenti tre test:
• Test numero 1: generazione effettuata con parametri di default.
- 77 -
• Test numero 2: generazione effettuata forzando le ricorsioni sui nodi di profondità
maggiore (omesse le ricorsioni sui nodi <sentence> e <clause>).
- 78 -
• Test numero 3: generazione effettuata eliminando totalmente le ricorsioni.
- 79 -
Dal confronto dei risultati ottenuti dai tre test precedenti (e tenendo conto di una serie di 10
test effettuati per ognuno dei tre casi) abbiamo ricavato che il numero e la tipologia dei
nodi generati possono essere in parte gestiti semplicemente intervenendo sui parametri di
ricorsione. La prima serie di test (di cui il test numero 1 fa parte) ha evidenziato la
prevalenza dei nodi di profondità più bassa nelle configurazioni di default. I nodi <turn>,
<sentence> e <clause> hanno il maggior numero di occorrenze essendo più facilmente
“raggiungibili” dalla funzione che genera i nodi. Forzando le ricorsioni sui nodi di profondità
più elevata (test numero 2 e relativa serie) abbiamo osservato che i nodi <PP> ed <NP>
riescono ad avere maggior numero di occorrenze in quanto il software non può effettuare
ricorsioni sui precedenti ed è forzato a “scendere” più in basso nell’albero per cercare nodi
dove applicare ricorsioni. Ovviamente data la probabilità che la funzione generatrice
“scenda” in quel determinato ramo, il numero di ricorsioni che si riescono a generare
scende notevolmente.
Nell terza serie di test sono state semplicemente eliminate le ricorsioni per dimostrare che
in tal caso la distribuzione della molteplicità dei nodi è del tutto casuale.
- 80 -
4.3 Verifica dei risultati ottenuti tramite il software XGATE
Allo scopo di verificare il corretto funzionamento del programma realizzato sono stati
effettuati dei test tramite il software XGATE, un tool realizzato in collaborazione con il
gruppo NLP del Dipartimento di Scienze Fisiche dell’Università Federico II e con il Dott.
Leandro D’Anna del DSLL dell’Università di Salerno.
XGATE nasce per risolvere il problema della gestione dei TreeBanks semi-strutturati,
memorizzati in formato XML. Il tool permette di effettuare operazioni sui documenti XML,
dal loro editing alla loro interrogazione.
XGATE si propone di aiutare coloro che devono creare basi di dati in XML vincolate da
una grammatica di tipo DTD ed è comprensivo di un supporto adattivo che permette la
creazione di query utili per interrogazioni dirette su XML tramite il linguaggio XPATH.
Nel nostro caso abbiamo utilizzato XGATE per verificare che i documenti XML prodotti dal
programma XML Recursive Generator rispettino i parametri inseriti dall’utente.
A tale scopo abbiamo effettuato i seguenti test.
• Test numero 1: Verifica delle percentuali di ricorsione generate
Obiettivo del test è quello di dimostrare che il numero di nodi che presentano
determinate ricorsioni è corretto.
Le immagini sottostanti mostrano le impostazioni utlizzate ed i risultati ottenuti dalla
generazione del documento XML:
- 81 -
Come si può notare dalle figure abbiamo forzato il software a generare un numero
massimo di nodi direttamente ricorsivi del 48,5% rispetto al totale dei nodi e tali nodi
possono essere solo del tipo <SENTENCE>. Abbiamo inoltre imposto che una
quantità pari ad un massimo del 48,5% dei nodi generati siano indirettamente
ricorsivi e al tempo stesso solamente del tipo <CLAUSE>.
Dall’esame del grafico riguardante i risultati della generazione deduciamo che il
software ha generato undici ricorsioni dirette del nodo <SENTENCE> (unico tipo di
nodo che poteva essere direttamente ricorsivo in base alle impostazioni iniziali) ed
una ricorsione indiretta del nodo <CLAUSE> (unico tipo di nodo che poteva essere
indirettamente ricorsivo in base alle impostazioni iniziali).
Eseguiamo adesso il programma XGATE, formulando alcune query per verificare la
correttezza dei risultati ottenuti.
La prime query che eseguiamo verificano la correttezza del numero dei nodi
<SENTENCE> e <CLAUSE>
Le due query eseguite per prime sono molto semplici:
//SENTENCE
//CLAUSE
Esse sono state utilizzate per ottenere tutti i nodi di tipo <SENTENCE> e
<CLAUSE> presenti nel documento a qualsiasi profondità.
- 82 -
Le immagini seguenti mostrano la stringa di query utilizzata ed i risultati ottenuti in
XGATE per quanto riguarda la prima delle due query:
Osservando i risultati ottenuti noteremo che la query ha restituito esattamente
ventotto nodi di tipo <SENTENCE>, valore che risulta essere uguale a quello
- 83 -
indicato nel grafico che mostra i dettagli della generazione del documento XML
mostrato in precedenza. Procediamo riportando i risultati della seconda query, che
restituisce tutte le istanze del nodo <CLAUSE>.
La query restituisce esattamente diciotto nodi <CLAUSE>, esattemente come ci si
aspettava dal grafico che mostra i dettagli della generazione del documento XML
mostrato in precedenza. Verifichiamo ora le ricorsioni dirette utilizzando la seguente
query:
//SENTENCE/SENTENCE
La query restituisce tutti i nodi <SENTENCE> che sono figli di <SENTENCE>
(quindi tutti i nodi <SENTENCE> che costituiscono ricorsioni dirette)
Dai calcoli precedentemente effettuati osservando il grafico che mostra i dettagli
della generazione del documento XML, il numero di tali ricorsioni dovrebbe essere
pari ad undici.
- 84 -
L’immagine seguente mostra il risultato della query:
Si potrà facilmente verificare che i nodi sono esattamente undici.
Verifichiamo anche l’esistenza dell’unica ricorsione indiretta tramite la seguente
query che restituisce i nodi <CLAUSE> che hanno un antenato <CLAUSE> che non
sia diretto:
//CLAUSE//*/CLAUSE
L’immagine seguente mostra il risultato: E’ stato restituito un unico nodo
<CLAUSE> come previsto.
- 85 -
• Test numero 2: Verifica del rispetto dei parametri di generazione
Obiettivo del test è quello di dimostrare che il documento XML ottenuto rispetti i
parametri di generazione impostati.
Le immagini sottostanti mostrano le impostazioni utlizzate ed i risultati ottenuti:
Come si può notare dalle figure precedenti, abbiamo imposto al programma di
generare nodi direttamente ricorsivi in numero massimo pari al 48,5% del totale dei
nodi, solamente del tipo <NP> e <PP>, con ricorsioni ammesse solo oltre il terzo
livello di profondità dell’albero XML. Il software ha generato un totale di otto
- 86 -
ricorsioni dirette distribuite casualmente sui due tipi di nodi sopracitati. Le ricorsioni
dirette sono state vietate ponendo la percentuale delle stesse rispetto al totale dei
nodi pari a 0%. L’altezza massima dell’albero è stata impostata ad un valore pari ad
otto. Verifichiamo i risultati ottenuti tramite XGATE ed alcune query sul documento
generato. La prima query che eseguiamo è utilie a dimostrare che non vi siano
ricorsioni a profondità minore di tre livelli. Essendo “deep” l’attributo che indica la
profondità dei nodi nella struttura XML e sapendo che le ricorsioni possono essere
generate solo sui nodi <NP> e <PP> le query da eseguire sono le seguenti:
//NP[@deep<3]
//PP[@deep<3]
Ecco il risultato di entrambe le query:
Si noterà dalle immagini che non viene restituito nessun valore, a testimonianza del
fatto che non vi sono nodi <PP> o <NP> nei primi tre livelli dell’albero XML.
- 87 -
Per verificare che nessun nodo abbia una profondità maggiore ad otto eseguiamo la
seguente query:
//*[@deep>8]
Ecco il risultato:
Dopo aver visionato l’immagine possiamo affermare che anche questa volta
l’assenza di nodi restituiti conferma la correttezza della nostra ipotesi. Per
completare il nostro test cerchiamo le ricorsioni generate e verifichiamo il loro
numero (che in base al grafico deve essere pari ad otto) direttamente dal
documento XML generato (NOTA: alcuni nodi sono stati compattati per comodità
anteponendovi un segno di “+”):
<?xml version="1.0" encoding="UTF-8"?> <!--Automatically generated with Recursive XML Generator v1.0--> <!DOCTYPE dialog SYSTEM "./dialog.dtd"> <dialog> <turn att1="" att2="" prgr="0" bre="1" deep="1"> <sentence att2="" att3="" prgr="1" bre="1" deep="2"> <clause att1="" att3="" prgr="2" bre="1" deep="3"> <UNP att2="" prgr="3" bre="1" deep="4">patra</UNP> <UNP att1="" att2="" prgr="6" bre="2" deep="4">e</UNP> <ADVP att1="" att2="" att3="" prgr="7" bre="3" deep="4">li</ADVP> <NP att1="" att3="" prgr="8" bre="4" deep="4"> <ADJP att1="" att2="" prgr="30" bre="3" deep="5">foco</ADJP> </NP> <VP att3="" prgr="9" bre="5" deep="4"> <PP att3="" prgr="16" bre="2" deep="5"> <PP att3="" prgr="79" bre="3" deep="6">me</PP> (1) </PP> </VP> <UNP att3="" prgr="74" bre="17" deep="4">compunto</UNP> </clause> <clause att1="" prgr="36" bre="7" deep="3"> <VP att2="" att3="" prgr="38" bre="11" deep="4"> <NP att3="" prgr="41" bre="6" deep="5">me</NP> </VP> </clause> </sentence> </turn>
- 88 -
<turn att1="" prgr="4" bre="2" deep="1"> <sentence att3="" prgr="5" bre="2" deep="2"> <clause att1="" prgr="10" bre="2" deep="3"> <UNP prgr="11" bre="6" deep="4">che</UNP> <PP att1="" att3="" prgr="12" bre="7" deep="4"> <PP prgr="13" bre="1" deep="5"> (2) <PP prgr="19" bre="1" deep="6"> (3) <NP prgr="21" bre="1" deep="7">e</NP> </PP> </PP> </PP> </clause> <clause att1="" att2="" prgr="23" bre="3" deep="3"> <VP att2="" prgr="29" bre="10" deep="4"> <PP att1="" prgr="34" bre="5" deep="5"> <PP att1="" prgr="50" bre="2" deep="6">e</PP> (4) </PP> <PP att1="" att2="" prgr="58" bre="9" deep="5">da</PP> <UNP att1="" prgr="65" bre="10" deep="5">si</UNP> </VP> </clause> <clause att1="" att2="" att3="" prgr="89" bre="18" deep="3" /> </sentence> </turn> +<turn att2="" prgr="14" bre="3" deep="1"> +<turn att1="" prgr="17" bre="4" deep="1"> <turn prgr="18" bre="5" deep="1"> <sentence att1="" att2="" prgr="20" bre="4" deep="2"> <clause att2="" att3="" prgr="24" bre="4" deep="3"> <PP att1="" att3="" prgr="47" bre="13" deep="4"> <UNP att1="" att2="" prgr="52" bre="7" deep="5">ferute</UNP> <PP att1="" att3="" prgr="83" bre="12" deep="5"> (5) <PP att1="" att2="" att3="" prgr="96" bre="4" deep="6">vergognosa</PP> (6) </PP> </PP> <ADVP prgr="84" bre="20" deep="4">grida</ADVP> </clause> </sentence> </turn> +<turn att1="" att2="" prgr="33" bre="6" deep="1"> <turn prgr="35" bre="7" deep="1"> <sentence att3="" prgr="48" bre="10" deep="2"> <clause att1="" att3="" prgr="55" bre="11" deep="3"> <NP att2="" att3="" prgr="73" bre="16" deep="4"> <NP att3="" prgr="90" bre="13" deep="5">pel</NP> (7) </NP> </clause> </sentence> </turn> <turn att1="" att3="" prgr="40" bre="8" deep="1"> <sentence att1="" att2="" prgr="44" bre="8" deep="2"> <clause att1="" att2="" prgr="46" bre="9" deep="3"> <NP att1="" att3="" prgr="51" bre="14" deep="4"> <NP att3="" prgr="78" bre="11" deep="5">per</NP> (8) </NP> </clause> <clause att1="" att2="" att3="" prgr="49" bre="10" deep="3" /> </sentence> <sentence att2="" prgr="91" bre="18" deep="2" /> </turn> <turn att2="" att3="" prgr="42" bre="9" deep="1"> <sentence att1="" att2="" prgr="45" bre="9" deep="2" /> </turn> +<turn att1="" prgr="53" bre="10" deep="1"> +<turn att3="" prgr="57" bre="11" deep="1"> +<turn att2="" prgr="60" bre="12" deep="1"> +<turn att1="" att2="" att3="" prgr="63" bre="13" deep="1"> +<turn att2="" prgr="67" bre="14" deep="1"> +<turn att1="" prgr="68" bre="15" deep="1" /> <turn att1="" prgr="87" bre="16" deep="1"> <sentence att1="" att2="" prgr="95" bre="20" deep="2" /> </turn> <turn att1="" att2="" att3="" prgr="88" bre="17" deep="1" /> <turn att2="" att3="" prgr="92" bre="18" deep="1"> <sentence att1="" att3="" prgr="93" bre="19" deep="2" /> </turn> </dialog>
Le ricorsioni trovate sono esattamente otto come previsto e tutte comprese
nell’intervallo stabilito. Non vi sono nodi che abbiano profondità maggiore di otto.
I risultati dei precedenti test ci permettono di affermare con certezza la piena e corretta
funzionalità del software per quanto riguarda la corretta generazione degli alberi XML.
- 89 -
Conclusioni
Come si evince dai test effettuati e dalla descrizione del software e delle sue funzionalità
(presentata nel capitolo 3), è possibile affermare che il programma è di utilizzo facile ed
intuitivo grazie all’interfaccia user-friendly oltre a possedere una robusta struttura logica
interna che gli permette di affrontare anche carichi di lavoro elevati senza andare in crash.
Il software ha inoltre risposto adeguatamente ai test sulle variazioni dell’input fornendo un
output adeguato che rispettava i parametri di generazione. Tali parametri, data la loro
molteplicità e natura permettono di generare strutture XML quasi completamente
“adattabili” a qualsivoglia tipologia gradita dall’utente, senza tralasciare che tutte le utility
incorporate nel software per visualizzare e analizzare i dati sull’XML generato ne rendono
sicuramente ancora più comodo e gradevole l’utilizo.
In conclusione, avendo rivisitato tutte le sue caratteristiche, possiamo sicuramente
affermare che il software* risulta ben funzionante e soddisfa pienamente gli obiettivi per cui
è stato progettato.
Fig.1 – Logo del software XML Recursive Generator
* NOTA: Il software è di tipo OpenSource e può essere liberamente scaricato dal sito http://www.parlaritaliano.it/
- 90 -
Appendice
A Caratteristiche tecniche del software
- 91 -
Tabella caratteristiche del software prodotto:
Tecnologia utilizzata DotNet 2.0, DOM, Framework. Linguaggio principale utilizzato C# Altri linguaggi utilizzati HTML, ActionScript, JavaScript Piattaforma di sviluppo utilizzata Microsoft Visual C# (Vers. utilizzata:
Express Edition 2005 v.8.0.50727.42)File di cui è costituito il pacchetto software (compilato)
RecursiveXMLGenerator.exe dett.html data.xmlrg dett.xmlrg dtd2xsd.dll IKVM.GNU.Classpath.dll IKVM.Runtime.dll saxon8.dll saxon8api.dll saxon8sa.dll text.txt
File di cui è costituito il codice sorgente del software
About.cs About.Designer.cs About.resx data.xmlrg Dett.cs Dett.Designer.cs dett.html Dett.resx dett.xmlrg dtd2xsd.dll Functions.cs MainForm.cs MainForm.Designer.cs MainForm.resx Program.cs Recursive XML Generator.csproj Recursive XML Generator.csproj.user Recursive XML Generator.sln Recursive XML Generator.suo saxon8.dll saxon8api.dll saxon8sa.dll ShowFile.cs ShowFile.Designer.cs ShowFile.resx
Dimensione totale del programma ~ 13,3 Mb Dimensione codice sorgente ~ 1700 LOC Versione analizzata 0.3 beta Data di rilascio 13-09-2007 Piattaforme hardware/software sulle quali è stato testato il software
Personal computer IBM compatibili con processori pentium III (866 Mhz) e IV (fino a 3.0 Ghz) con architettura x86 a 32 bit. Ram da 256 MB a 1GB, scheda video SVGA. SO: WinXP SP2.
Requisiti hardware/software minimi Personal computer IBM compatibile con processore x86 a 32 bit. Ram 256 MB, 10 Mb di spazio su disco. Windows XP, WebBrowser con FlashPlayer e JavaScript abilitato.
- 92 -
Appendice
B Glossario di riferimento delle abbreviazioni e
dei termini tecnici
93
# . #PCDATA – PCDATA è una parola chiave riservata alla DTD per "Parsed Character Data", che indica del testo generico. .NET – Vedi DotNet Albero – Struttura dati (grafo) senza cicli, composta da nodi aventi unico padre e zero o più figli.
A Axis – Parametro che indica il senso di percorrenza di un albero XML.
C C# – Linguaggio di programmazione ad oggetti utilizzato dalla piattaforma .NET. C++ – Linguaggio di programmazione ad oggetti. Canonical XML – Algoritmo per verificare l’identicità di due documenti XML.
D Dangling Pointer – Problema derivante dal rilascio erroneo di memoria ancora in uso. DOM – Document Object Model. E’ una forma di rappresentazione dei documenti strutturati come modello orientato agli oggetti. DotNET – Tecnologia Microsoft che permette di sviluppare qualsiasi tipo di applicazione: stand-alone, client/server, per sistemi handheld (palmari, smart phones, smart devices), per il web. DTD – Document Type Definition (Definizione del tipo di documento)
E ECMA - La ECMA (European Computer Manufacturers Association) International è un’associazione dedicata alla standardizzazione delle informazioni e dei sistemi di comunicazione.
F FLWOR – Espressioni XQuery di interrogazione nelle quali vengono utilizzati i costrutti For, Let, Where, Order By e Return. Framework – Struttura di supporto su cui un software può essere organizzato e progettato. Alla base di un framework c'è una serie di librerie di codice utilizzabili con uno o più linguaggi di programmazione, spesso corredate da una serie di strumenti di supporto allo sviluppo del software
G Garbage collector - Per garbage collection (letteralmente raccolta dei rifiuti, a volte abbreviato con GC) si intende una modalità automatica di gestione della memoria, mediante la quale un sistema operativo, o un compilatore e un modulo di run-time, liberano le porzioni di memoria che non dovranno più essere successivamente utilizzate dalle applicazioni. Grafo – Struttura dati costituita da un insieme di nodi e di archi che connettono coppie di nodi senza intersecarsi tra loro. GUI – Graphic User Interface.
94
H HTML – HyperText Markup Language (Linguaggio di markup basato su ipertesti)
I ISO – Organizzazione Internazionale per le Standardizzazioni.
J Java – Linguaggio di programmazione ad oggetti.
L Link – Collegamento
M Markup – Marcatura. In generale un linguaggio di markup descrive i meccanismi di rappresentazione (strutturali, semantici o presentazionali) del testo che, avendo convenzioni standardizzate, sono utilizzabili su più supporti. Memory Leak – Problema derivante dal mancato rilascio di memoria non più accessibile.
N Namespace – Spazio dei nomi, è - nella terminologia relativa all'informatica - una collezione di nomi di entità, definite dal programmatore, omogeneamente usate in uno o più file
sorgente. Nodo – Elemento di una struttura dati.
R Ricorsione – Proprietà di un algoritmo: viene detto algoritmo ricorsivo un algoritmo espresso in termini di se stesso, ovvero in cui l'esecuzione dell'algoritmo su un insieme di dati comporta la semplificazione o suddivisione dell'insieme di dati e l'applicazione dello stesso algoritmo agli insiemi di dati semplificati.
S SGML – Standard Generalized Markup Languages
T Tag - elemento sintattico con cui si marcano porzioni di un documento nei linguaggi di markup
U UML – Unified Modeling Language. ("linguaggio di modellazione unificato") è un linguaggio di modellazione e specifica basato sul paradigma object-oriented. URI – Un Uniform Resource Identifier (URI, acronimo più generico rispetto ad "URL") è una stringa che identifica univocamente una risorsa generica. URL – Un Uniform Resource Locator o URL è una sequenza di caratteri che identifica univocamente l'indirizzo di una risorsa in Internet, come un documento o un'immagine.
95
URN – Un URN (Uniform Resource Name) è un URI che identifica una risorsa mediante un "nome" in un particolare dominio di nomi (detto "namespace").
W W3C – World Wide Web Consortium: Associazione costituita con lo scopo di migliorare gli esistenti protocolli e linguaggi per il WWW e di aiutare il Web a sviluppare tutte le sue potenzialità.
X Xfragment – Protocollo in fase di sviluppo per la canonizzazione di frazioni di documenti XML. XHTML – Riformulazione di HTM con applicazioni XML XLink – Linguaggio XML based per definire Link XML – eXstensible Markup Language. XMLRG – XML Recursive Generator (Software per la generazione di documenti XML Ricorsivi). XMLSchema – Schema utilizzato per definire la grammatica di un documento XML XPath – Linguaggio tramite il quale è possibile eseguire delle espressioni per indirizzare parti di un documento XML. XPointer – XML Pointer Language (Linguaggio di indirizzamento per XML). XQuery – XML Query Language (Linguaggio di query per XML). XQueryX – Versione di XQuery con sintassi XML. XSL – eXstensible Stylesheet Language (Linguaggio di annotazione
per trasformare/convertire documenti XML).
- 96 -
Appendice
C Estratti di codice sorgente
- 97 -
private void findRecursionsGraph(string startnode)
// Create a list of the nodes using BFS visit { string u; proc_nodes.Add(startnode); nodesq.Enqueue(startnode); ins_dis(startnode, 0, ref nod_dis); while (nodesq.Count > 0) { u = (string)nodesq.Dequeue(); ArrayList Values = new ArrayList(); ElementDecl ed = dtd.FindElement(u); if (ed != null) scorri_gruppi(ed.ContentModel.Model, ref Values); foreach (string str in Values) { if ((!proc_nodes.Contains(str)) && (!vis_nodes.Contains(str))) { proc_nodes.Add(str); nodesq.Enqueue(str); ins_dis(str, ric_dis(u,ref nod_dis) + 1, ref nod_dis); } else if ((u == str) && (!dirr_nodes.Contains(str))) { dirr_nodes.Add(str); } } vis_nodes.Add(u); } findCycles(); } private void findCycles() // Finds indirect recursions (cycles) executing BFS algorythm starting // each time from a different node of the graph { BegRecLev = vis_nodes.Count; bool recflag = false; foreach (string node in vis_nodes) { ArrayList vis_nodes_tc = new ArrayList(); ArrayList proc_nodes = new ArrayList(); Queue nodesq = new Queue(); string u; proc_nodes.Add(node); nodesq.Enqueue(node); while (nodesq.Count > 0) { u = (string)nodesq.Dequeue(); ArrayList Values = new ArrayList(); ElementDecl ed = dtd.FindElement(u); if (ed != null) scorri_gruppi(ed.ContentModel.Model, ref Values); foreach (string str in Values) { if ((!proc_nodes.Contains(str)) && (!vis_nodes_tc.Contains(str))) { proc_nodes.Add(str); nodesq.Enqueue(str); } else if ((str != u) && (node == str) && (!indr_nodes.Contains(str))) { if (ric_dis(str, ref nod_dis) < BegRecLev) BegRecLev = ric_dis(str, ref nod_dis); indr_nodes.Add(str); } else if ((proc_nodes.Contains(str)) || (vis_nodes_tc.Contains(str))) { recflag = true; // A recursion was found if (ric_dis(str, ref nod_dis) < BegRecLev) { BegRecLev = ric_dis(str, ref nod_dis); BegRecNode = str; levBtwRecursions(str); } } } vis_nodes_tc.Add(u); } } if (recflag == false) BegRecLev = 0; // DTD without recursions }
- 98 -
private void levBtwRecursions(string node) // Obtain number of levels between // a node (passed as argument) and // its recursion in the graph { bool btwflag = false; ArrayList vis_nodes_lbr = new ArrayList(); ArrayList proc_nodes = new ArrayList(); Queue nodesq = new Queue(); string u; ins_dis(node, 0, ref nod_dis_tmp); proc_nodes.Add(node); nodesq.Enqueue(node); while (nodesq.Count > 0) { u = (string)nodesq.Dequeue(); ArrayList Values = new ArrayList(); ElementDecl ed = dtd.FindElement(u); if (ed != null) scorri_gruppi(ed.ContentModel.Model, ref Values); foreach (string str in Values) { if ((!proc_nodes.Contains(str)) && (!vis_nodes_lbr.Contains(str))) { proc_nodes.Add(str); nodesq.Enqueue(str); ins_dis(str, ric_dis(u, ref nod_dis_tmp) + 1, ref nod_dis_tmp); } else if (btwflag == false) {LevBtwRec = ric_dis(u, ref nod_dis_tmp) + 1; btwflag = true;} } vis_nodes_lbr.Add(u); } }
private void CarDTDRoot() // Find the DTD root node { bool trovata=false; ArrayList Values = new ArrayList(); string[] Names; pars_res.Text = "Recupero caratteristiche DTD avviato...\n"; foreach (string key in dtd.getTable().Keys) { ElementDecl ed = (ElementDecl)dtd.getTable()[key]; if (ed != null) scorri_gruppi(ed.ContentModel.Model, ref Values); } Names = (string[])Values.ToArray(typeof(string)); foreach (string key in dtd.getTable().Keys) { for (int i = 0; i < Names.Length; i++) if (Names[i] == key) trovata = true; if (trovata == false) // Root node indentified. { pars_res.Text +="Nodo root identificato...<"+key+">\n"; dtdroot = key; return; } trovata = false; } MessageBox.Show("Nella DTD di input non è possibile identificare la root.",
"Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }
private string RndWordFromFile(string file) // Extract a random word from a text file { StreamReader sr = new StreamReader(file); string s; s = sr.ReadToEnd(); sr.Close(); string[] split = s.Split(new Char[] {' ', '\n', ';', ':', '.', ',', '\0', '\r', '\'', '\"', '!', '?'}); do s = split[rndnum(0, split.Length - 1)]; while (s == ""); return s; }
- 99 -
private bool XMLGen()
// Generate XML { if (saveFileDialog1.ShowDialog() == DialogResult.OK) { if (saveFileDialog1.FileName != "") {
for (int j = 0; j < nod_graph.GetLength(0); j++) { nod_graph[j, 0] = null; nod_graph[j, 1] = null; }
DettagliXML.Enabled = true; xmlpath = saveFileDialog1.FileName; FileInfo src = new FileInfo(xmlpath); XMLStatus.Text = src.Name; XmlTextWriter xmlWriter = new XmlTextWriter(xmlpath, System.Text.Encoding.UTF8); xmlWriter.Formatting = Formatting.Indented; xmlWriter.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"); xmlWriter.WriteComment("Automatically generated with Recursive XML Generator"); xmlWriter.WriteStartElement(dtdroot); xmlWriter.WriteEndElement(); xmlWriter.Close(); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(xmlpath); XmlNode root = xmlDoc.DocumentElement; NodeCounter = 0; RemNodes = Convert.ToInt32(node_num.Text); progressBar1.Minimum = 0; progressBar1.Maximum = RemNodes; for (int i = 0; i < (Convert.ToInt32(node_num.Text)); i++) { attr_nodes.Clear(); deepReached = 1; RemNodes = addNodeRnd(root, xmlDoc, RemNodes, i, 0); progressBar1.Value = i+1; } addRndTails(xmlDoc,root); xmlDoc.Save(xmlpath); XmlDocument xmlDatDoc = new XmlDocument(); xmlDatDoc.RemoveAll(); XmlTextWriter xw = new XmlTextWriter(XMLGFILE, System.Text.Encoding.UTF8); xw.Formatting = Formatting.Indented; xw.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"); xw.WriteStartElement("chart"); xw.WriteEndElement(); xw.Close(); xmlDatDoc.Load(XMLGFILE); XmlElement datroot = xmlDatDoc.DocumentElement; datroot.RemoveAll(); datroot.SetAttribute("caption", "Dettagli XML Generato"); datroot.SetAttribute("subcaption", "Nodi gen. = " + NodeCounter + " | Nodi rim. = " + RemNodes + " | Ric. dir. rim. " + drec_nodes + " su " + ((int)((Convert.ToDecimal(dir_rec.Text) * Convert.ToInt32(node_num.Text)) / 100)) + " | Ric. indir. rim. " + irec_nodes + " su " + ((int)((Convert.ToDecimal(indir_rec.Text) * Convert.ToInt32(node_num.Text)) / 100))); datroot.SetAttribute("yAxisName", "Numero nodi"); datroot.SetAttribute("xAxisName", "Nome nodi"); int c=0; while (nod_graph[c,0] != null) { XmlElement childNode = xmlDatDoc.CreateElement("set"); childNode.SetAttribute("label", nod_graph[c,0].ToString()); childNode.SetAttribute("value", nod_graph[c,1].ToString()); datroot.AppendChild(childNode); c++; } xmlDatDoc.Save(XMLGFILE); pars_res.Text += "Generazione file XML...\n"; pars_res.Text += "Numero nodi generati [" + NodeCounter + "]\n"; pars_res.Text += "Num. nodi rimanenti [" + RemNodes + "]\n"; pars_res.Text += "Num. ric. dir. rim. [" + drec_nodes + "] su [" + ((int)((Convert.ToDecimal(dir_rec.Text) * Convert.ToInt32(node_num.Text)) / 100)) + "]\n"; pars_res.Text += "Num. ric. indir. rim. [" + irec_nodes + "] su [" + ((int)((Convert.ToDecimal(indir_rec.Text) * Convert.ToInt32(node_num.Text)) / 100)) + "]\n"; outputLog(LOGFILE); return true; } } return false; }
- 100 -
private void addRndTails(XmlDocument xd, XmlNode xnode) //Add random tails to XML tree { if (nodeCanBeTail(xnode.Name) && (xnode.ChildNodes.Count == 0)) { XmlText tx = xd.CreateTextNode(RndWordFromFile(TEXTFILE)); xnode.AppendChild(tx); return; } else if (!nodeCanBeTail(xnode.Name) && (xnode.ChildNodes.Count == 0)) return; foreach (XmlNode xmlnd in xnode.ChildNodes) addRndTails(xd,xmlnd); }
private bool nodeCanBeTail(string node) // Return true if node child can be #PCDATA (node can be tail) { string line = ""; TextReader tr = File.OpenText(dtdPath); do { line = tr.ReadLine(); if (line == null) break; if (line.IndexOf("<!ELEMENT " + node) != -1) { if (line.IndexOf("#PCDATA") != -1) return true; else return false; } } while (line != null); tr.Close(); return false; } private bool childRespectTreeSize(XmlNode node) // Return true if a child respect tree parameters { //if (node.ChildNodes.Count > Convert.ToInt32(MaxEspOrizz.Text)) return false; if (nodeDeep(node) > (Convert.ToInt32(MaxEspVert.Text)-1)) return false; if ((treeBreadth((nodeDeep(node) + 1), node) > (Convert.ToInt32(MaxEspOrizz.Text))))
return false; if ((treeBreadth((nodeDeep(node) + 1), node) < (Convert.ToInt32(MinEspOrizz.Text))))
return false; return true; }
private int treeBreadthCalc(int deep, XmlNode node, int currDeep) // Calculate breadth at specified level (passing root node as argument) { if (currDeep == deep) { return 1; } else { int bre = 0; foreach (XmlNode nd in node.ChildNodes) bre = bre +
treeBreadthCalc(deep, nd, (currDeep + 1)); return bre; } } private int nodeDeep(XmlNode node) // Return node deep { if (node.ParentNode == null) return 0; else return (1 + nodeDeep(node.ParentNode)); }
- 101 -
private int addNodeRnd(XmlNode rootNode, XmlDocument xd, int RemNodes, int progr, int call) // Add "random" node (conditioned by parameters and DTD) { string rootn, str; rootn = rootNode.Name; ArrayList Values = new ArrayList(); ElementDecl ed = dtd.FindElement(rootn); if (ed != null) scorri_gruppi(ed.ContentModel.Model, ref Values); if (Values.Count > 0) { if (rootNode.ChildNodes.Count == 0) { if ((str = wichChildIns(rootn, RemNodes, ref Values, rootNode)) != "None") { XmlElement childNode = xd.CreateElement(str); [...settaggio attributi (omesso per comodità)] rootNode.AppendChild(childNode); addNodeToList(childNode.Name); NodeCounter++; return (RemNodes-1); } else { if (rootNode.ParentNode != null) { XmlNode fatherNode = rootNode.ParentNode; attr_nodes.Remove(rootNode.ParentNode.Name); deepReached--; if (call < OLIMIT) RemNodes = addNodeRnd(fatherNode, xd,
RemNodes, progr, (call + 1)); else return RemNodes; } else return RemNodes; } } else { if (rndnum(0, 9) == 0) { if ((str = wichChildIns(rootn, RemNodes, ref Values, rootNode)) != "None") { XmlElement childNode = xd.CreateElement(str); [...settaggio attributi (omesso per comodità)] rootNode.AppendChild(childNode); addNodeToList(childNode.Name); NodeCounter++; return (RemNodes-1); } else { if (rootNode.ParentNode != null) { XmlNode fatherNode = rootNode.ParentNode; attr_nodes.Remove(rootNode.ParentNode.Name); deepReached--; if (call < OLIMIT) RemNodes = addNodeRnd(fatherNode, xd, RemNodes,
progr, (call + 1)); else return RemNodes; } else { return RemNodes; } } } else { XmlNode childNode = rootNode.ChildNodes[rndnum(0, (rootNode.ChildNodes.Count - 1))]; attr_nodes.Add(rootn); deepReached++; if (call < OLIMIT) RemNodes = addNodeRnd(childNode, xd, RemNodes, progr, (call + 1)); else return RemNodes; } } } else { if (rootNode.ParentNode != null) { XmlNode fatherNode = rootNode.ParentNode; attr_nodes.Remove(rootNode.ParentNode.Name); deepReached--; if (call < OLIMIT) RemNodes = addNodeRnd(fatherNode, xd, RemNodes, progr, (call + 1)); else return RemNodes; } else {return RemNodes;}} return RemNodes;}
- 102 -
private string wichChildIns(string nodeFather, int remNodes, ref ArrayList Values, XmlNode rootNode)
// Return a child can be inserted { ArrayList strDir = new ArrayList(); foreach (string str in Values) strDir.Add(str); ArrayList strInd = new ArrayList(); foreach (string str in Values) strInd.Add(str); if (Values.Count == 0) return "None"; if ((drec_nodes > 0)||(irec_nodes > 0)) { if ((drec_nodes > 0) && (rndnum(0, 9) < 5)) { foreach (string str_val in Values) { if (str_val == nodeFather) { if (dir_rec_lbx.CheckedItems.Contains("<" + str_val + ">")) { if ((rndnum(0, 9) < 5)&&(deepReached >=
Convert.ToInt32(LivMinRic.Text))&&(deepReached <= Convert.ToInt32(LivMaxRic.Text))&&(childRespectTreeSize(rootNode)))
{ drec_nodes--; return str_val; } else { if (remNodes > drec_nodes) { foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if ((strDir.Count == 0)||(!childRespectTreeSize(rootNode)))
return "None"; else return ((string)strDir[rndnum(0, (strDir.Count - 1))]); } else { if ((deepReached >= Convert.ToInt32(LivMinRic.Text))&&(deepReached <= Convert.ToInt32(LivMaxRic.Text))&& (childRespectTreeSize(rootNode))) { drec_nodes--; return str_val; } } } } else { foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if (childRespectTreeSize(rootNode)) return ((string)strDir[rndnum(0, (strDir.Count - 1))]); else return "None"; } } } } [continua nella pagina successiva...]
- 103 -
[...continua da pagina precedente]
if ((irec_nodes > 0) && (rndnum(0, 9) >= 5)) { for (int i = 0; i < Values.Count; i++) { string str_val = (string)strInd[rndnum(0, (strInd.Count - 1))]; strInd.Remove(str_val); if ((str_val != nodeFather) && (attr_nodes.Contains(str_val))) { if (ind_rec_lbx.CheckedItems.Contains("<" + str_val + ">")) {
if ((rndnum(0, 9) < 5)&&(deepReached >= Convert.ToInt32(LivMinRic.Text))&&(deepReached <= Convert.ToInt32(LivMaxRic.Text))&&(childRespectTreeSize(rootNode)))
{ irec_nodes--; return str_val; } else { if (remNodes > irec_nodes) { foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if ((strDir.Count == 0)||(!childRespectTreeSize(rootNode)))
return "None"; else return ((string)strDir[rndnum(0, (strDir.Count - 1))]); } else { if ((deepReached >=
Convert.ToInt32(LivMinRic.Text))&&(deepReached <= Convert.ToInt32(LivMaxRic.Text))&& (childRespectTreeSize(rootNode)))
{ irec_nodes--; return str_val; } } } } else { foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if ((strDir.Count == 0)||(!childRespectTreeSize(rootNode)))
return "None"; else return ((string)strDir[rndnum(0, (strDir.Count - 1))]); } } } } foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if ((strDir.Count == 0)||(!childRespectTreeSize(rootNode))) return "None"; return ((string)strDir[rndnum(0, (strDir.Count - 1))]); } else { foreach (string str in Values) { if (str == nodeFather) strDir.Remove(str); if (attr_nodes.Contains(str)) strDir.Remove(str); } if ((strDir.Count == 0)||(!childRespectTreeSize(rootNode))) return "None"; else return ((string)strDir[rndnum(0, (strDir.Count - 1))]); } }
- 104 -
Bibliografia
Siti web
Sito internet: http://msdn.microsoft.com
Sito internet: http://www.wikipedia.org/
Sito internet: http://www.w3.org/
Sito internet: http://www.codeproject.com/
Sito internet: http://www.dotnethell.it/
Sito internet: http://www.mrwebmaster.it/
Sito internet: http://msdn2.microsoft.com/en-us/default.aspx
Libri
John Sharp, “Microsoft Visual C# 2005”, Mondadori (novembre, 2006)
Paolo Pialorsi, “Programmare con XML”, Mondadori (maggio, 2004)
Elliotte Rusty Harold, W.Scott, Means, “Guida di riferimento XML”, Apogeo (2001)
W3C, “XQuery 2.0 Specification” (http://www.w3.org/TR/2005/WD-xpath20-20050915.htm)
- 105 -
Ringraziamenti
Uno speciale ringraziamento ai miei genitori, che mi hanno premurosamente accudito
e supportato materialmente nell’arco di tutta la mia carriera universitaria.
Ringrazio inoltre il prof. Francesco Cutugno, del Dipartimento di Scienze Fisiche dell’Università
Federico II di Napoli, ed il dott. Leandro D’Anna dell’Università di Salerno per la loro disponibilità e
comprensione, durante tutto il periodo del tirocinio e della stesura della tesi.
Un opportuno ringraziamento voglio dedicarlo anche a tutti i miei amici e compagni di studio di questi
anni, senza il supporto dei quali molti ostacoli sarebbero stati sicuramente più ardui da superare; in
particolare voglio ricordare: Ciro Aversa, Mario Landolfo, Valerio Maggio, Gianluca Montesarchio,
Raffaele Moscariello, Mario Notomista, Giuseppe Palomba, Nicola Palumbo, Luca Pellecchia,
Antonio Vuolo.
Un ultimo e particolare ringraziamento a Stefania, che ha sempre creduto fermamente in me fino al
raggiungimento di questo importante traguardo.
Raffaele Liguoro
Napoli – 26 Settembre 2007