Modificare le datasource RDF

Modifica di datasource

Le darasource hanno quattro metodi che possono essere usate per modificare una datasource. E' possibile modificare una datasource usanto solo questi quattro metodi. Non tute le datasource sono modificabili. Queste datasource lanceranno un'eccezione quando tenterai di chiamare i metodi. Per esempio, i files RDF/XML caricati da un sito remoto non possono essere modificati direttamente.

Per aggiungere una tripla RDF ad una datasource, usa il metodo Assert. Dato un soggetto, un predicato e un complemento, questo metodo aggiungerà quella dichiarazione alla datasource. Se la dichiarazione esiste già, viene aggiunta di nuovo. Questo significha che tu potresti desiderare di usare prima il metodo GetTarget per vedere se esiste la dichiarazione. Nella terminologia di Mozilla, una tripla RDF o dichiarazione, viene chiamata asserzione, è da questo che il metodo prende il suo nome. Essenzialmente, stiamo dichiarando una qualche informazione.

Diciamo che volevamo aggiungere un nome per una persona. Ecco un esempio di come farlo.

var subject = rdfService.GetResource("http://www.xulplanet.com/rdf/people/David");
var predicate = rdfService.GetResource("http://www.xulplanet.com/rdf/people/name");
var name = rdfService.GetLiteral("David");

datasource.Assert(subject, predicate, name, true);

Primo, otteniamo gli oggetti risorsa per il soggetto e il predicato. Vogliamo aggiungere un nome, così usiamo il predicato 'name' qualificato con lo spazio di nomi. Il nome stesso è un letterale, così otterremo un oggetto letterale. I tre sono recuperati dal servizio RDF. Potremmo aver usato GetAnonymousResource per ottenere il soggetto, se avessimo voluto la certezza che l'URI della risorsa fosse unico.

Infine, viene chiamato Assert che richiede quattro argomenti, il soggetto, il predicato, il complemento, e il valore true. Questo ultimo argomeo  può essere utile per specificare che qualcosa è falso. Questo raramente è utile, perciò il quarto argomento dovrebbe sempre essere vero.

Le datasource potrebbero non accettare il cambiamento. Per esempio, quando una datasource non è scrivibile o se tenti di aggiungere dati non validi, la datasource rifiuterà il cambiamento. In codice nativo, puoi accorgertene, perché la datasource ritornerà un codice di statoNS_RDF_ASSERTION_REJECTED. Questo codice di stato, comunque non è un errore.

Per rimuovere una dichiarazione da una datasource, c'è un metodo simile Unassert. Questo metodo rimuoverà una sola dichiarazione in cui coincidano gli argomenti soggetto, predicato e complemento forniti. Per esempio, potremmo rimuovere la precedente dichiarazione in questo modo:

datasource.Unassert(subject, predicate, name);

Non importa se la dichiarazione esiste o no, il metodo Unassert non causerà un errore se non esiete.

Il metodo Change può essere usato per cambiare il complemento di una dichiarazione in un altro valore. Per esempio, questo può essere usato per cambiare il nome di una persona in un altro valore. Questo è equivalente alla rimozione del vecchio valore chiamando Unassert e alla successiva aggiunta di un nuovo valore chiamando Assert. Comunque, il metodo Change fa tutto in un solo passaggio.

Ecco un esempio:

var subject = rdfService.GetResource("http://www.xulplanet.com/rdf/people/George");
var predicate = rdfService.GetResource("http://www.xulplanet.com/rdf/people/name");
var oldName = rdfService.GetLiteral("George");
var newName = rdfService.GetLiteral("Georgina");

datasource.Change(subject, predicate, oldName, newName);

In questo caso, cambiamo il valore del predicato name per la risorsa 'George' dal valore 'George' al valore 'Georgina'. Il vecchio valore verrà rimosso dalla datasource e rimpiazzato con il nuovo valore.

Infine, il metodo Move può essere usato per cambiare la sorgente di una dichiarazione ad un altro valore. Può essere usato per cambiare una risorsa che abbia un dato nome. Per esempio, potremmo decidere che il nome di Georgina dovrebbe in realtà essere associato con la risorsa Georgina.

var oldSubject = rdfService.GetResource("http://www.xulplanet.com/rdf/people/George");
var newSubject = rdfService.GetResource("http://www.xulplanet.com/rdf/people/Georgina");
var predicate = rdfService.GetResource("http://www.xulplanet.com/rdf/people/name");
var name= rdfService.GetLiteral("Georgina");

datasource.Move(oldSubject, newSubject, predicate, name);

Come per il metodo Change, il vecchio valore viene rimosso e viene aggiunto il nuovo valore. Il metodo Move funziona in senso inverso, cioè cambia le sorgenti invece dei complementi.

I metodi Change e Move non cambiano se il vecchio valore no è presente. Per entrambe i metodi, verrà comunque aggiunto il nuovo valore. A causa di questo, dovresti prima assicurarti che il vecchio valore esiste, se questo ti interessa.

Esempio di modifica

Diciamo che stai costruendo un catalogo di foto. Potresti avere una varietà di informazioni da memorizzare per ogni foto, come il posto, la data e una descrizione della foto. Per ogni foto avremo una risorsa e un certo numero di proprietà. Le proprietà non devono essere le stesse per ogni foto, così possiamo tralasciare alcune cose per qualche foto e aggiungere più dettagli per altre. Un interfaccia utente che mostra i dati potrebbe mostrare solo i campi presenti.

Prima di tutto, iniziamo creando una nuova in-memory datasource vuota:

var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"].
getService(Components.interfaces.nsIRDFService);

var photosDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
.createInstance(Components.interfaces.nsIRDFDataSource);

Poi, aggiungiamo qualche dato per ogni foto. Potremmo usare GetAnonymousResource per generare URI anonimi per ogni foto. Oppure, potremmo usare l'URI che indica dove si trova la foto all'interno di un sito. Potremmo usare entrambe, visto che RDF usa l'URI solo come segnaposto e non fa un download del contenuto. Nel nostro esempio, prendiamo URI di esempio comehttp://www.example.com/image/clown.jpeg.

La prima foto ha due proprietà, una data e una descrizione. Avremo bisogno di fare due chiamate al metodo Assert per aggiungere queste informazioni. Per esempio:

var photoRes = rdfService.GetResource("http://www.example.com/image/clown.jpeg");
var dateProp = rdfService.GetResource("http://www.example.com/rdfns/date");
var descriptionProp = rdfService.GetResource("http://www.example.com/rdfns/description");

var photoDate = rdfService.GetLiteral("January 20, 2003");
var photoDescription = rdfService.GetLiteral(
"The clown at Simone's birthday party makes a funny face.");

photosDS.Assert(photoRes, dateProp, photoDate, true);
photosDS.Assert(photoRes, descriptionProp, photoDescription, true);

Prima di tutto, otteniamo gli oggetti risorsa e letterale di cui abbiamo bisogno. Per la data potevamo usre un oggetto  nsIRDFDate invece del letterale , ma non è importante per questo esempio.

Le due linee Assert aggiungono rispettivamente la data e la descrizione. Se decidessimo più tardi di rimuovere una proprietà useremmo il metodo Unassert.

Aggiungiamo una seconda foto, in questo caso scattata nello stesso giorno.

var photo2Res = rdfService.GetResource("http://www.example.com/image/cake.jpeg");

var photo2Description = rdfService.GetLiteral(
"Simone blows out the candles on her cake to officially mark that she is four.");

photosDS.Assert(photo2Res, dateProp, photoDate, true);
photosDS.Assert(photo2Res, descriptionProp, photo2Description, true);

In questo caso, non abbiamo bisogno di ottenere di nuovo tutti gli oggetti risorsa e letterale, visto che possiamo riutilizzare quelli di prima. Non c'è alcun vantaggio a chiedere ripetutamente  questi oggetti al servizio RDF, visto che il servizio ritornerà comunque lo stesso oggetto. Anche la data è la stessa, così possiamo riutilizzare lo stesso oggetto. Il metodo Assert viene chiamato due volte, come prima, ma usando i dati della seconda foto.

Siccome entrambe le date sono uguali, possiamo fare una ricerca per tutte le foto scattate in quel giorno usando il metodo GetSources. Il codice di esempio sotto ritornerà un elenco con due elementi, visto che ci sono due foto per la data 'January 20, 2003'. Nota che la stringa deve coincidere perfettamente.

var sources = photosDS.GetSources(dateProp, photoDate, true);
while (sources.hasMoreElements()){
var photoRes = sources.getNext();
if (photoRes instanceof Components.interfaces.nsIRDFResource){
var description = photosDS.GetTarget(photoRes, descriptionProp, true);
if (description instanceof Components.interfaces.nsIRDFLiteral){
alert(description.Value);
}
}
}

Per ogni elemento nell'elenco, chiamiamo il metodo GetTarget per ottenere la descrizione della foto. Data solo la data della foto, avremo trovato le due descrizioni. Questi tipi di ricerche permettono di navigare facilmente attraverso le informazioni di un grafico RDF.

Poi, deciciamo di cambiare la descrizione della prima foto. Non vogliamo creare una nuova risorsa e aggiungere di nuovo i dati. Invece useremo il metodo Change per ottenere questo risultato. Questo metodo permetterà di cambiare il valore di una proprietà da un valore all'altro. Non possiamo chiamare solo Assert, perché verrebbe aggiunto un secondo valore e non verrebbe rimosso l'altro. Questo, comunque potrebbe essere utile in qualche caso. Ecco un esempio di modifica di un lavore.

var photoNewDescription = rdfService.GetLiteral(
"Simone laughs when the clown at her birthday party makes a funny face.");

photosDS.Change(photoRes, descriptionProp, photoDescription, photoNewDescription);

Un pò di tempo dopo, potremmo trovare che le descrizioni delle foto sono invertite. Potremmo usare il metodo Move per cambiarle così che le descrizioni siano associate con le giuste risorse. E' anche possibile usare Unassert con i vecchi valori e Assert per rimettere i valori nel posto originale.

photosDS.Move(photoRes, photo2Res, descriptionProp, photoNewDescription);
photosDS.Move(photo2Res, photoRes, descriptionProp, photo2Description);

La prima dichiarazione sposta la prima descrizione dalla prima risorsa foto alla seconda risorsa foto. La seconda dichiarazione sposta la seconda descrizione dalla seconda foto alla prima. Questo è un esempio piuttosto semplice, ma sicuramente utile. Un esempio più efficace dell'uso di Move è quando si muove una risorsa da un posto ad uno diverso. Per esempio, se le foto sono memorizzate in una serie di gruppi, si può muovere una foto da un gruppo all'altro usando il metodo Move per cambiare il gruppo con cui era associato. Per esempio:

var oldFolderRes = rdfService.GetResource("http://www.example.com/folder/unsorted");
var newFolderRes = rdfService.GetResource("http://www.example.com/folder/simonesbirthday");
var photoChildProp = rdfService.GetResource("http://www.example.com/rdfns/photo");

photosDS.Move(oldFolderRes, newFolderRes, photoChildProp, photoRes);

Questo esempio muoverà una foto da un gruppo all'altro. Potremmo usare codice simile per muovere un gruppo in un altro gruppo. In questo caso stiamo usando una proprietà per indicare che una foto è in un gruppo. Potemmmo invece voler usare un contenitore RDF. Questo viene descritto nella prossima sezione.

Osservare i cambiamenti delle datasource

Le datasource RDF possono avere uno o più osservatori collegati. Gli osservatori sono chiamati ogni volta che cambia la datasource. Questo può essere utile per controllare le datasource fornite da Mozilla o le tue datasource. Per esempio, potrebbe aiutare a tenere una parte di codice separata da un'altra, se necessario. Il costruttore di template di XUL usa gli osservatori per controllare i cambiamenti della datasource RDF così che i contenuti di ogni template possano essere ricostruiti.

Puoi aggiungere un osservatore ad una datasource con il metodo AddObserver di una datasource. Puoi rimuoverlo usando il metodo RemoveObserver. Le datasource possono avere diversi osservatori e saranno tutti chiamati quando la datasource cambierà. Gli osservatori dovrebbero implementare metodi dell'interfaccia  nsIRDFObserver.

L'osservatore riceve una notifica separata per ogni modifica alla datasource. Gli argomenti forniti ai metodi dell'osservatore indicano che cosa è cambiato. L'osservatore dovrebbe implementare un metodo per oguno dei quattro tipi di cambiamenti che possono avvenire in una datasource, come descritto prima. Questi cambiamenti sono affermazione, cancellazione di affermazione, cambiamento e modifica. nsIRDFObserver ha quattro metodi che corrispondono a questi quattro tipi, con prefisso 'on'. Per esempio il metodo onAssert verrà chiamato ogni volta che viene fatta una chiamata Assert sulla datasource.

Due metodi addizionali, onBeginUpdateBatch e onEndUpdateBatch sono chiamati quando sono chiamati i metodi corrispondenti di una datasource. Anche se non c'è niente di speciale che questi metodi abbiamo bisogno di fare, sono un indicatore per la datasource e i suoi osservatori che si è in procinto di fare un gran numero di cambiamenti alla datasource. Siccome potrebbe essere poco performante gestire ogni cambiamento, i metodi di batching ti permettono di capire quando un gruppo di cambiamenti inizia e finisce, e ottimizzare il codice per questo caso. Per esempio, quando inizia un'operazione seriata, il costruttore di template XUL non ricostruisce il contenuto di nessun template finché non termina l'operazione seriata. Se i cambiamenti non fossero fatti in gruppo, il costruttore ricostruirebbe il template ad ogni cambiamento.

Devi implementare i sei metodi dell'interfaccia  nsIRDFObserver, anche se non hai bisogno di inserire nessuna azione in ognuno. Ecco un esempio:

var observer = {
onAssert : function(ds, source, predicate, target)
{
var dateProp = rdfService.GetResource("http://www.example.com/rdfns/date");
var photoDate = rdfService.GetLiteral("January 20, 2003");
if ((dateProp == predicate) && (photoDate == target)){
alert("That is Simone's birthday!");
}
},
onUnassert : function(ds, source, predicate, target){},
onChange : function(ds, source, predicate, oldTarget, newTarget){},
onMove : function(ds, oldSource, newSource, predicate, target){},
onBeginUpdateBatch : function(ds){},
onEndUpdateBatch : function(ds){}
};

photosDS.AddObserver(observer);

In questo esempio, vogliamo solo tenere sotto controllo del dichiarazioni che vengono aggiunte alla datasource, perciò non dobbiamo fare niente negli altri metodi. Abbiamo ancora bisogno di dichiararli, oppure verranno generati degli errori. Gli argomenti del metodo onAssert indicano i dati che vengono aggiungi. Questo viene usato per confrontare la data con una data specifica e mostrare un avviso se la data è 'January 20,  2003'. Nota che possiamo comparare risorse e letterali usando l'operatore ==.

La in-memory-datasource implementa l'interfaccia nsIRDFPropagatableDataSource. Ha una sola proprietà propagateChanges che può essere impostata a true o false. L'impostazione predefinita è true, ma se la cambi a false, la notificazione all'osservatore sarà disattivata. Questo disabiliterà tutte le notifiche agli osservatori, così che non saranno chiamati. Impostare il valore di nuovo a true riabiliterà le notificazioni. I cambiamenti fatti mentre il valore è false non raggiungeranno gli osservatori. Come esempio, la datasource bookmarks disabilita le notificazioni quando riordina una cartella, visto che viene spostata e mescolata una grande qantità di dati durante l'operazione di ordinamento.