xulfr.org

8.3 Manipulation de sources de données RDF

Écrit par Neil Deakin. Traduit par Adrien Montoille (07/09/2004).
Page originale : http://www.xulplanet.com/tutorials/xultu/rdfscript.html xulplanet.com

Cette section explique comment manipuler RDF avec un script.

Sources de données RDF avec XPCOM

Les gabarits peuvent être utilisés pour extraire les données d'une source RDF et construire du contenu à partir de celles-ci. Cependant les sources de données peuvent aussi être parcourues à partir d'un script. Vous pouvez obtenir la source de données d'un élément construit à partir d'un gabarit et en sélectionner des ressources. Cela vous permet aussi de modifier la source de données.

Le composant XPCOM pour RDF propose un certain nombre d'interfaces. Sont listées ci-aprés certaines de ces interfaces :

nsIRDFService
Un service global de RDF. Il est utilisé pour générer des objets qui peuvent identifier de façon unique une ressource pointant vers une source de données RDF.
nsIRDFDataSource
Represente une source de données RDF, soit intégrée, soit provenant d'un fichier RDF. Des méthodes vous permettent d'obtenir ou de placer des valeurs.
nsIRDFContainer
Un noeud conteneur dans une source de données RDF. Des méthodes vous permettent d'ajouter et de supprimer des ressources.
nsIRDFContainerUtils
Cette interface a quelques méthodes pratiques de conteneur pour créer des ressources Seq, Bag et Alt.

Dans la fenêtre de recherche de fichiers, nous pouvons implémenter la possibilité de stocker les éléments les plus récemment recherchés. Le champs de saisie de recherche peut-être remplacé par une liste déroulante éditable qui contient une énumération des termes qui ont été récemment recherchés. Nous allons maintenant ajouter cette fonctionnalité.

Cela ne fonctionnera réellement que si la fenêtre a accés à un espace sur le disque où la liste des éléments récemments recherchés peut-être enregistrée. Les endroits les plus suceptibles de permettre ceci sont le répertoire de profil de l'utilisateur ou un répertoire choisi par l'utilisateur lui-même. Bien que nous n'allons pas l'utiliser ici, le répertoire du profil de l'utilisateur peut être trouvé en utilisant le composant @mozilla.org/file/directory_service;1. Pour simplifier l'exemple, nous allons juste mettre un chemin de fichier quelconque dans un attribut datasources.

Nous pourrions enregistrer la liste des recherches récentes dans un fichier texte simple. Cependant, nous pouvons utiliser RDF qui a déjà la capacité de lire et d'écrire ses données et de mettre à jour un composant graphique généré automatiquement à partir d'un gabarit.

Premièrement, les changements dans le fichier XUL. Nous allons remplacer le champs de saisie par une liste déroulante éditable. Changez la valeur de l'attribut datasources par un chemin approprié. (Le fichier doit déjà exister.)

            
<menulist id="find-text" flex="1" style="min-width: 15em;"
             editable="true"
             datasources="file:///mozilla/recents.rdf"
             ref="http://www.xulplanet.com/rdf/recent/all">

  <template>
    <menupopup>
      <menuitem label="rdf:http://www.xulplanet.com/rdf/recent#Label" uri="rdf:*"/>
    </menupopup>
  </template>
</menulist>
            
        

Tous les éléments XUL qui ont leur fils générés par un gabarit ont une propriété database qui se référe à un objet nsIRDFDataSource. Cet objet peut ensuite être utilisé pour lire et modifier la source de données utilisée. La propriété database est placée sur l'élément qui porte l'attribut datasources. Cela va typiquement être un élément tree, ou comme c'est le cas ici, un élément menulist.

La propriété database contient une liste (en fait un nsISimpleEnumerator) de chacune des sources de données qui ont été spécifiées dans l'attribut datasources. Cela signifie que nous avons besoin de réitérer chaque élément, même s'il n'y en a qu'un seul. L'exemple suivant montre comment réaliser ceci en supposant qu'il n'existe qu'une source de données :

var dsource;
var menulist=document.getElementById("find-text");
var sources=menulist.database.GetDataSources();

if (sources.hasMoreElements()){
    dsource=sources.getNext();
}
dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);
        
    

Tout d'abord, nous obtenons une référence sur un menulist, qui ici porte l'identifiant find-text (attribut id). Ensuite nous obtenons la liste des sources de données à partir de l'élément menulist. L'interface nsISimpleEnumerator a deux méthodes (pareillement à l'interface d'énumération java). Nous bouclons sur les éléments de l'énumération et, parce que nous supposons qu'il n'y a en a qu'un, nous allons juste l'obtenir par la méthode getNext. Finalement nous appellons QueryInterface pour s'assurer qu'il s'agit d'un nsIRDFDataSource.

Nous allons utiliser un code similaire pour créer la liste des recherches récentes. D'abord, initialisons les composants que nous voulons utiliser. Nous aurons besoin de trois composants. L'interface nsIRDFService va être utilisée pour créer les objets de ressource, l'interface nsIRDFContainer va servir à ajouter des ressources à  la source de données et nous utiliserons la troisième interface, nsIRDFContainerUtils, uniquement lorsque la liste est employée la première fois, pour créer le noeud racine. Ajoutez le code suivant au début d'un fichier de script (findfile.js). Il va être exécuté quand la fenêtre de recherche de fichiers est chargée.

const RDFC = '@mozilla.org/rdf/container;1';
RDFC = Components.classes[RDFC].createInstance(Components.interfaces.nsIRDFContainer);

const RDFCUtils = '@mozilla.org/rdf/container-utils;1';
RDFCUtils = Components.classes[RDFCUtils].getService(Components.interfaces.nsIRDFContainerUtils);

const RDF = '@mozilla.org/rdf/rdf-service;1';
RDF = Components.classes[RDF].getService(Components.interfaces.nsIRDFService);
            
        

Cette portion de code va créer les trois services dont nous avons besoin. La syntaxe est similaire à celle utilisée pour les autres créations d'objets XPCOM. Les trois premières lignes prennent une référence à un objet nsIRDFContainer. Puis nous exécutons une opération semblable pour obtenir l'objet nsIRDFContainerUtils. Enfin nous la faisons de nouveau pour l'objet nsIRDFService.

Ensuite, nous créons une fonction d'initialisation que nous allons appeler dans l'événement onload de la fenêtre. Elle va être exécutée quand la fenêtre va être affichée. Dans ce source, nous allons ajouter le code pour initialiser les objets RDF que nous avons créé plus haut.

findfile.xul : 
            
<window onload="initSearchList()" ... >
            
findfile.js : 
            
var dsource;

function initSearchList()
{
    var recentlist=document.getElementById("find-text");
    var sources=recentlist.database.GetDataSources();
    var rootnode=RDF.GetResource("http://www.xulplanet.com/rdf/recent/all");

    while (sources.hasMoreElements()){
        try {
            dsource=sources.getNext();
            dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);

            RDFC.Init(dsource,rootnode);
        } catch (e) {
            RDFCUtils.MakeSeq(dsource,rootnode);
            RDFC.Init(dsource,rootnode);
        }
    }
}
            
        

Décomposons la fonction initSearchList :

var recentlist=document.getElementById("find-text");
var sources=recentlist.database.GetDataSources();
Tout d'abord, nous obtenons une référence sur l'élément menulist sur lequel est attachée la source de données. Elle a une propriété database qui contient les sources de données qui sont présentes. Nous obtenons une référence sur les sources de données disponibles et nous l'assignons à la variable sources.
var rootnode=RDF.GetResource("http://www.xulplanet.com/rdf/recent/all");
Un objet de ressource est généré avec l'URI donnée. Il sera l'élément racine, un élément RDF Seq qui contient une liste de ressources, une pour chaque objet présent dans la liste des recherches récentes. Cette fonction ne retire rien de la source de données, elle convertit juste une URI en un identifiant de ressource. Au lieu d'inscrire l'URI dans le code, nous pourrions aussi l'obtenir à  partir de l'attribut ref.
while (sources.hasMoreElements()){
    try {
        dsource=sources.getNext();
        dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);
Aprés, nous bouclons sur chaque source de données afin d'obtenir la bonne.
 RDFC.Init(dsource,rootnode);
Cette fonction initialise le conteneur RDF (l'interface nsIRDFContainer) avec la source de données et le noeud racine. Plus tard, nous pouvons utiliser l'objet pour ajouter de nouvelles ressources à l'interieur du conteneur. Nous aurons besoin de faire cela pour ajouter un article recherché à la source de données. Une erreur se produira si la source de données ou le noeud racine n'existe pas (par exemple, si le fichier RDF n'a pas été trouvé). Le code a été mis dans un block "try-catch" pour assurer la gestion des erreurs.
} catch (e){
    RDFCUtils.MakeSeq(dsource,rootnode);
    RDFC.Init(dsource,rootnode);
}
Si une erreur se produit, c'est trés probablement parce que le noeud racine n'existe pas. Pour le créer, nous appellons la méthode MakeSeq de l'interface nsIRDFContainerUtils. Des fonctions similaires existent pour créer des éléments Bag et Alt. (MakeBag et MakeAlt). On essaie ensuite de réinitialiser le conteneur.

L'interface nsIRDFService contient une méthode GetResource qui crée pour nous un objet de ressource à  partir de la chaîne de caractères passée en argument. Cette méthode ne récupére aucune valeur, mais convertit simplement une chaîne de caractères en un objet ressource que l'on peut utiliser pour obtenir la valeur à partir de la source de données. Les interfaces RDF n'employent pas de chaînes de caractères mais utilisent plutôt des ressources pour se référer au contenu. La valeur retournée par GetResource est de type nsIRDFResource.

Maintenant que les objets ont été initialisés, nous pouvons ajouter et enlever des informations à la source de données. Il y a deux méthodes requises selon si vous voulez ajoutez une ressource à un conteneur ou si vous voulez ajouter une liaison d'une ressource vers une autre. Ces deux cas correspondent à l'ajout d'un marque-page et à l'ajout d'une propriété comme un URL ou un titre à un marque-page.

Nous allons ajouter une nouvelle entrée à la liste des éléments recherchés quand l'utilisateur cliquera sur le bouton de recherche. Nous le simplifierons un peu à l'extrême de plusieurs manières. Tout d'abord nous ne prendrons pas la peine de vérifier les entrées doubles. Deuxièmement, nous ne nous soucierons pas de limiter la longueur de la liste.

Ajoutons une autre fonction qui est appelée à  partir de la fonction doFind :

            
function doFind()
{
    var recentlist=document.getElementById("find-text");
    var fldval=recentlist.value;

    addSearchedItem(fldval);
    
.
.
.
            
        

Cette portion de code prend la valeur saisie dans la liste déroulante éditable et nous passons le texte à la fonction addSearchedItem qui va être définie plus loin.

            
function addSearchedItem(txt)
{
    var newnode=RDF.GetResource("http://www.xulplanet.com/rdf/recent/all/item"+(RDFC.GetCount()+1));
    var labelprop=RDF.GetResource("http://www.xulplanet.com/rdf/recent#Label");
    var newvalue=RDF.GetLiteral(txt);

    dsource.Assert(newnode,labelprop,newvalue,true);
    RDFC.InsertElementAt(newnode,1,true);

    dsource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
}
            
        

Ce code fait trois choses, il ajoute une nouvelle ressource, il ajoute un nouvel enregistrement qui contient la valeur, et ensuite il enregistre la source de données modifiée. Décomposons le code :

var newnode=RDF.GetResource("http://www.xulplanet.com/rdf/recent/all/item"+(RDFC.GetCount()+1));
Cette ligne crée un objet ressource pour la ressource qui va être ajoutée. La fonction GetCount retourne le nombre de ressources déjà présentes dans le conteneur. Cela nous permet de générer une URI unique. Nous pouvons aussi appeler GetAnonymousResource (à  la place de GetResource) qui ne prend pas de paramètre et génère au hasard une URI unique.
var labelprop=RDF.GetResource("http://www.xulplanet.com/rdf/recent#Label");
Nous placerons dans la propriété Label de la ressource le texte qui a été récemment cherché. Vous pouvez utiliser n'importe quel nom de propriété pourvu qu'il soit conforme. Vous noterez qu'il a la même valeur que l'attribut label de l'élément menuitem ajouté précédemment au fichier XUL.
var newvalue=RDF.GetLiteral(txt);
La fonction GetLiteral génére un objet RDF de type chaîne qui va contenir le texte que l'utilisateur a recherché et passé à  travers l'argument txt. Nous n'utilisons pas la fonction GetResource ici car nous assignons une valeur à une ressource.
dsource.Assert(newnode,labelprop,newvalue,true);
Cette ligne va ajouter une liaison à la source de données RDF. Dans ce cas, elle dit que la propriété Label de la ressource http://www.xulplanet.com/rdf/recent/all/itemX est l'objet littéral qui a été créé à la ligne précédente, où X est le nombre retourné par la fonction GetCount. Cependant, ce n'est que la moitié de ce qu'il faut faire. Nous devons encore dire que la ressource est une des recherches récentes.
RDFC.InsertElementAt(newnode,1,true);
Cette ligne ajoute la ressource au conteneur. Ici, nous l'insérons en position 1. (Notons que le premier élément est 1 et non 0.) Nous pouvons l'insérer partout, ou appeler AppendElement à  la place pour l'ajouter à  la fin. Le gabarit menulist va maintenant détecter la nouvelle ressource, et ajoutera une rangée suplémentaire dans la liste.
dsource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
Enfin, nous écrivons la source de données sur le disque en utilisant la fonction Flush. Cette fonction ne fait pas partie de l'interface nsIRDFDataSource, donc nous devons d'abord appeller QueryInterface pour convertir la source de données vers la bonne interface, nsIRDFRemoteDataSource.

Toutes les sources de données ne peuvent pas être modifiées. Toutes les sources de données chargées à partir de fichiers et d'URLs de ressource peuvent être écrites aussi bien que des sources de données internes.

Si vous ouvrez la fenêtre de recherche de fichiers, que vous entrez du texte puis pressez "Recherche", vous constaterez que le texte apparait en tant qu'un des choix dans la liste déroulante. Même si vous quittez puis redémarrez, le texte restera dans la liste déroulante.

Pour vérifier les entrées dupliquées, nous pouvons vérifier les ressources existantes, en utilisant les fonctions hasAssertion ou GetAllRessources de l'interface nsIRDFDataSource.

Exemple de recherche de fichier : Source Voir


Nous allons voir maintenant comment accéder au presse-papier du système pour les opérations copier et coller.