Contenitori RDF

Interfacce contenitore RDF

Siccome i contenitori RDF, cioè, i tipi Seq, Bag e Alt, sono spesso manipolati, Mozilla fornisce alcuni metodi addizionali per gestire questi tipi. Questi metodi sono gestiti da due interfacce, nsIRDFContainer e nsIRDFContainerUtils. E' importante notare che queste interfacce sono solo metodi di praticità costruiti intorno ai metodi delle datasource descritti in precedenza. E' possibile fare tutte queste operazioni senza usare per niente le classi contenitore. Questo può essere utile per fare cose più specifiche con in contenitori. Per la maggior parte degli usi, comunque queste classi forniscono una via comoda per manipolare i contenitori RDF. Dire che i contenitori RDF sono solo un'estensione dei metodi delle datasource significa che tutte le datasource supportano i contenitori, anche se non tutte li useranno per qualche motivo. Tutti gli osservatori collegati alla datasource riceveranno notifiche riguardanti i cambiamenti sottostanti che fa un contenitore.

L'interfaccia nsIRDFContainer è usata per tenere un contenitore RDF. Puoi usare questa interfaccia per ricercare, aggiungere e rimuovere gli elementi figli di un contenitore. Questa interfaccia è utile, perché gestisce per te all'indicizzazione degli elementi. Per crearne una, usa questo codice:

var container = Components.classes["@mozilla.org/rdf/container;1"].
createInstance(Components.interfaces.nsIRDFContainer);

Il codice sopra creerà un contenitore RDF non inizializzato. L'inizializzazione è descritta sotto.

Il componente contenitore RDF (nsIRDFContainer) dovrebbe essere creato come istanza con createInstance, non come servizio. Ci sono diverse sorgenti e esempi che usano erroneamente getService.

L'interfaccia nsIRDFContainerUtils ha alcuni metodi convenienti per creare contenitori e vedere se una risorsa è o non è un contenitore. Questo oggetto è un servizio, perciò dovrebbe essere creato con getService.

var rdfContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
getService(Components.interfaces.nsIRDFContainerUtils);

Ricercare in un contenitore

Ci sono due modi di inizializzare un oggetto contenitore RDF. Il primo è chiamare il metdo Init dell'interfaccia nsIRDFContainer. Questo metodo prende una risorsa e inizializza il contenitore usando quella risorsa. In questo caso, la risorsa deve già essere un contenitore. Se una risorsa non è un contenitore, il metodo Init lancerà un'eccezione.

var folderRes = rdfService.GetResource("http://www.example.com/folder/simonesbirthday");

var container = Components.classes["@mozilla.org/rdf/container;1"].
createInstance(Components.interfaces.nsIRDFContainer);
try {
container.Init(photosDS, folderRes);
}
catch (ex){}

In questo esempio, il contenitore è inizializzato in una data risorsa. I due argomenti per il metodo Init sono rispettivamente la datasource e la risorsa. Dobbiamo inserire la chiamata in un blocco try-catch nel caso la risorsa non sia un contenitore RDF. Se hai la certezza che lo sia, non è necessario controllare.

Il secondo modo di inizializzare un oggetto contenirore RDF è crearne uno nuovo. Questo metodo trasformerà una risorsa esistente in un contenitore. Dovresti usare questo metodo quando crei nuovi contenitori. Questo richiede l'uso dei tre metodi nell'interfaccia nsIRDFContainerUtils, MaseSeq, MakeBag, e MakeAlt. Quale usare dipende da che tipo di contenitore vuoi creare. Per esempio, il metodo MakeSeq trasformerà una risorsa in Seq. Ricorda, che i contenitori RDF sono strati sopra gli altri metodi delle datasource. E' possibile usare i metodi delle datasource per far diventare una risorsa un contenitore RDF.

var folderRes = rdfService.GetResource("http://www.example.com/folder/simonesbirthday");

var rdfContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
createInstance(Components.interfaces.nsIRDFContainerUtils);

var container = rdfContainerUtils.MakeSeq(photosDS, folderRes);

Il metodo MakeSeq prende gli argomenti datasource e risorsa come accade per il metodo Init del contenitore RDF. Questo metodo ritorna un nuovo oggetto contenitore, già inizializzato con i valori corretti. Se la risorsa è già un contenitore, i tre metodi Make ritornano solo il contenitore esistente. Non lo creano di nuovo né cambiano da un tipo di contenitore a un'altro. Questo significa che è possibile creare e ottenere conenitori esistenti usando soltanto i metodi Make.

Puoi controllare se una risorsa è un contenitore o meno usando i metodi Is dell'interfaccia nsIRDFContainerUtils. Più precisamente, IsSeq controlla se una risorsa è una Seq, IsBag controlla se una risorsa è un Bag, IsAlt controlla se è Alt e IsContainer controlla se una risorsa è un qualunque tipo di contenitore. I quattor metodi ritornano true oppure false.

Puoi trovare quali risorse sono elementi figli di un contenitore usando il metodo GetElements di un contenitore. Come gli altri metodi di ricerca RDF, ritorna un elenco che può essere usato per controllare uno per uno gli elementi figli di un contenitore.  Saranno ritornati in ordine, anche se per un Bag, questo ordine non è importante.

var ratingProp = rdfService.GetResource("http://www.example.com/rdfns/rating");
var threeProp = rdfService.GetLiteral("3");

var children = container.GetElements();
while (children.hasMoreElements()){
var child = children.getNext();
if (child instanceof Components.interfaces.nsIRDFResource){
photosDS.Assert(child, ratingProp, threeProp, true);
}
}

Questo codice controlla uno per uno tutti i figli di un contenitore. Per ogni figlio, aggiunge una tripla, impostando il valore 'rating' a 3.

GetCount è un ulteriore metodo di un contenitore, che può essere usato per ottenere il numero di figli nel contenitore senza doverli esaminare. In realtà non è proprio così. In realtà ritorna l'indice dell'ultimo figlio nel contenitore. Ricorda che in un contenitore non devono essere usati tutti gli indici e che alcuni possono essere usati più di una volta. Se vuoi controlare se un contenitore ha qualche figlio, usa il metodo IsEmpty dell'interfaccia nsIRDFContainerUtils. Questo metodo ritorna true o false.

Puoi voler recuperare uno specifico figlio da un contenitore, identificato dal proprio inidice. Ricorda dalla sezione del modello RDF che i contenitori RDF si riferiscono ai figli usando predicati come _1, _2 e così via. Questo rende piuttosto semplice il recupero di un figlio specifico usando GetTarget senza usare le classi contenitore. Infatti, le classi contenitore non contengono un metodo come GetChild per recuperare i figli.

var kidsRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/KarensKids");
var twoRes = rdfService.GetResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#_2");

var child = datasource.GetTarget(kidsRes, twoRes, true);

L'esempio sopra può essere usato per recuperare il secondo figlio di un contenitore. Usando l'esempio di Karen dalle sezioni precedenti, ritornerà il secondo figlio di Karen. Questa tecnica non è molto diversa dal recupero di qualunque altra proprietà di una risorsa. L'interfaccia nsIRDFContainerUtils ci fornisce un metodo pratico per creare indicizzare risorse anche se nella forma del metodo IndexToOrdinalResource. Per esempio, potremmo recuperare la risorsa 'two' usando la seguente forma.

var twoRes = rdfContainerUtils.IndexToOrdinalResource(2);

Questo può rendere il codice più leggibile. C'è anche un metodo simile OrdinalResourceToIndex per andare in direzione opposta e ritornare l'indice da una risorsa. Naturalmente questo metodo fallirà in risorse non-ordinali. Puoi controllare se una risorsa è una risorsa ordinale con il metodo IsOrdinalProperty. Nota che gli indici nell'API RDF iniziano sempre con 1 non con zero.

Puoi determinare l'indice di un figlio in un contenitore usando il metodo del container IndexOf. Questo metodo ritornerà l'intero invece del figlio nel contenitore. Se il figlio non è nel contenitore, il metodo ritorna -1. Questo significa che puoi anche usare questo metodo per vedere se un figlio esiste nel genitore. C'è un metodo analogo indexOf nell'interfaccia nsIRDFContainerUtils che fa la stessa cosa solo che non devi prima creare un oggetto contenitore RDF. Nota la differenza di maiuscole/minuscole tra le due forme. Il seguente esempio determina la posizione di Sandra nella lista di figli di Karen.

var kidsRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/KarensKids");
var sandraRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/Sandra");

var idx = rdfContainerUtils.indexOf(datasource,kidsRes,sandraRes);

Potresti voler determinare qual è il genitore di un figlio, o determinare il contenitore in cui si trova una risorsa. Le classi contenitore RDF non forniscono un metodo per farlo. Un modo per determinare il contenitore per un figlio è questa:

var rdfContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
getService(Components.interfaces.nsIRDFContainerUtils);

var sandraRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/Sandra");
var parent = null;

var arcsIn = datasource.ArcLabelsIn(sandraRes);
while (arcsIn.hasMoreElements()){
var arc = arcsIn.getNext();
if (arc instanceof Components.interfaces.nsIRDFResource){
if (rdfContainerUtils.IsOrdinalProperty(arc)){
parent = datasource.GetSource(arc, sandraRes, true);
break;
}
}
}

Questo codice attraversa tutti i predicati (archi) che puntano alla risorsa 'Sandra'. Questa lista di predicati può includere un numero di cose. Se il predicato è una risorsa ordinale, comunque, ne deduciamo che è figlia di qualche altra risorsa. Possiamo usare il metodo GetResource per determinare il genitore. Ricorda che una risorsa può essere contemporaneamente in diversi contenitori. Questo esempio postula che il figlio avrà un solo genitore. Se vuoi trovare tutti i genitori, avrai invece bisogno di usare GetSources e costruire una lista.

Modificare un contenitore

Aggiungere e rimuovere figli da un contenitore è semplice. Ci sono diversi metodi dell'interfaccia nsIRDFContainer che possono essere usati per aggiungere e rimuovere i figli.

Per aggiungere un figlio a un contenitore usa il metodo AppendElement. Questo aggiungerà il figlio alla fine della lista di figli del genitore. L'indice ordinale sarà maggiore di una unità rispetto al più alto originale, perciò non dovrai calcolarlo. Questo metodo richiede un argomento, il figlio da aggiungere. Ecco un esempio:

var kidsRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/KarensKids");
var christaRes = rdfService.GetResource("http://www.xulplanet.com/rdf/people/Christa");

var container = Components.classes["@mozilla.org/rdf/container;1"].
createInstance(Components.interfaces.nsIRDFContainer);
try {
container.Init(datasource, kidsRes);
container.AppendElement(christaRes);
}
catch (ex){}

I metodi di modifica del contenitore RDF non controllano se il figlio è già nel contenitore. Questo significa che puoi aggiungere un figlio più di una volta. Il metodo InsertElementAt può essere usato per inserire un figlio ad un indice specifico.

container.InsertElementAt(christaRes,2,true);

Questo metodo richiede tre argomenti. Il primo argomento è la risorsa figlio da aggiungere. Il secondo è l'intero che indica la posizione in cui inserire il figlio. Il terzo argomento indica se rinumerare gli indici degli altri figli per accomodarsi al nuovo figlio. Ricorda che gli indici sono solo risorse predicato con una convenzione di numerazione e potrebbero esserci diversi figli con lo stesso indice. Se passi true come terzo argomento i figli rimanenti verranno rinumerati per accomodarsi al nuovo figlio. Nell'esempio sopra il nuovo figlio viene aggiunto alla seconda posizione. Il figlio in seconda posizione avrà un indice modificato a tre, il terzo figlio sarà mosso in quarta posizione, e così via. Se l'ultimo argomento è false gli indici non sono rinumerati. Questo significherebbe che ci sarebbero due figli al secondo indice nell'esempio sopra, nel caso uno esistesse già.

Il processo di rinumerazione è in grado di contemplare il caso in cui siversi indici che devono essere rinumerati hanno già diversi figli con lo stesso indice. Per esempio se ci fossero tre risorse con indice 3, verrebbero tutte spostate all'indice 4. Quelle all'indice 4 verrebbero mosse all'indice 5, e così via. Se la datasource implementa l'interfaccia nsIRDFPropagatableDataSource, le notifiche di cambiamento sono disattivate mentre viene eseguita la rinumerazione per evitare molte notifiche superflue. Verrà inviata agli osservatori solo la dichiarazione dell'inserimento della risorsa.

Per rimuovere un figlio usa i metodi RemoveElement o RemoveElementAt. Il primo rimuoverà un figlio, data la sua risorsa, mentre l'ultimo rimuoverà un figlio dato il suo indice.

container.RemoveElement(christaRes,true);
container.RemoveElementAt(2,true);

Posto che la risorsa sia quella inserita nell'esempio precedente, entrambe queste dichiarazioni faranno la stessa cosa. Il primo rimuove il figlio data la risorsa. Questo metodo determinerà l'indice da solo. La seconda dichiarazione rimuoverà un elemento ad un indice specifico. Al contrario dell metodo RemoveElement, il metodo RemoveElementAt ritornerà l'elemento rimosso come nodo RDF. Se ci sono diversi figli all'indice, il metodo RemoveElementAt rimuoverà solo uno di loro.

Entrambe i metodi di rimozione prendono anche un secondo argomento che indica se rinumerare gli altri figli una volta effettuata la rimozione, e questo funziona in maniera analoga a quanto accade per il metodo InsertElementAt.