Inhaltsverzeichnis

Trees mit RDF

Mit Xul können Trees erstellt werden. Diese können mit verschiedenen Datasourcen gefüttert werden. (XML, RDF, etc)

Hier ein Beispiel wie ein Tree mit RDF als Datenquelle definiert wird:

<tree id="mytree" seltype="single" hidecolumnpicker="false" flex="1"
   datasources="http://www.oesi.org/xul/rdf/adresse.rdf.php"
   ref="http://www.oesi.org/adresse/liste"
   flags="dont-build-content"
   enableColumnDrag="true"
   persist="hidden, height"
   height="200"
   >
 
   <treecols>
 
      <treecol id="kontakt-adressen-treecol-strasse" 
               label="Strasse" flex="1" hidden="false"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-plz" 
               label="Plz" flex="1" hidden="false"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-ort" 
               label="Ort" flex="1" hidden="false"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-nation" 
               label="Nation" flex="1" hidden="true"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-zustelladresse" 
               label="Zustelladresse" flex="1" hidden="false"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-adresse_id" 
               label="Adresse_id" flex="1" hidden="true"/>
      <splitter class="tree-splitter"/>
      <treecol id="kontakt-adressen-treecol-person_id" 
               label="Person_id" flex="1" hidden="true"/>
      <splitter class="tree-splitter"/>
   </treecols>
 
   <template>
      <rule>
         <treechildren>
            <treeitem uri="rdf:*">
 
	    <treerow>
 
	       <treecell label="rdf:http://www.oesi.org/adresse/rdf#strasse" />
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#plz" />
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#ort" />
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#nation" />
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#zustelladresse"  
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#adresse_id" />
               <treecell label="rdf:http://www.oesi.org/adresse/rdf#person_id" />
            </treerow>
 
            </treeitem>
	</treechildren>
      </rule>
   </template>
</tree>

So siehts aus:

Hier einige Tipps falls der Tree keine Daten anzeigt.

Aktualisieren des Trees

Zum Aktualisieren der Daten setzen wir die Datasource einfach neu.

Um das Caching von RDFs zu verhindern, hänge ich an die URL einen aktuellen Timestamp an:

function gettimestamp()
{
	var now = new Date();
	var ret = now.getHours()*60*60*60;
	ret = ret + now.getMinutes()*60*60;
	ret = ret + now.getSeconds()*60;
	ret = ret + now.getMilliseconds();
	return ret;
}
 
function refreshTree()
{
	document.getElementById("kontakt-adressen-tree").setAttribute("datasource","http://www.oesi.org/xul/rdf/adresse.rdf.php"+gettimestamp());
}

Diese Methode bringt allerdings folgende Probleme mit sich: - da die Daten asynchron in den Tree geladen werden weiss ich nicht ab wann alle Daten geladen wurden - bei zunehmender Datenmenge dauert die Aktualisierung relativ lange - Fehler beim Laden können nicht abgefangen werden

Um diese Probleme zu umgehen, sind einige Änderungen erforderlich. An die Datasource wird zusätzlich ein XMLSinkObserver angehängt. Dieser informiert uns wenn die Daten fertig geladen wurden. Zusätzlich hängen wir noch einen Listener an den Tree. Von diesem werden wir dann darüber informiert, wenn die Daten im Tree aktualisiert wurden.

var KontaktAdressenTreeSinkObserver =
{
	onBeginLoad : function(pSink) {},
	onInterrupt : function(pSink) {},
	onResume : function(pSink) {},
	onError : function(pSink, pStatus, pError) { alert('Fehler beim Laden der Daten '+pError); },
	onEndLoad : function(pSink)
	{
		//wenn die Daten in der Datasource aktualisiert wurden, kann  der Tree neu aufgebaut werden
		//dies erledigt die Funtkion rebuild() für uns:
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		document.getElementById('kontakt-adressen-tree').builder.rebuild();
	}
};
 
var KontaktAdressenTreeListener =
{
	willRebuild : function(builder) {  },
	didRebuild : function(builder)	{
  	     //die aktualisierung des Trees ist nun abgeschlossen.
  	     //ab nun koennen per Script wieder Eintraege markiert werden etc.
  	     alert('Die Daten sind jetzt auf dem neuesten Stand');
	}
};
 
var AdressenTreeDatasource;
 
onLoad()
{
	url = "http://www.oesi.org/xul/rdf/adresse.rdf.php";
 
	//Tree Element holen
	var treeAdressen=document.getElementById('kontakt-adressen-tree');
 
	var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
	//Datasource holen
	AdressenTreeDatasource = rdfService.GetDataSource(url);
 
	AdressenTreeDatasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
	AdressenTreeDatasource.QueryInterface(Components.interfaces.nsIRDFXMLSink);	
 
	//Datasource an den Tree haengen
	treeAdressen.database.AddDataSource(AdressenTreeDatasource);	
 
	//Observer an die Datasource haengen
	AdressenTreeDatasource.addXMLSinkObserver(KontaktAdressenTreeSinkObserver);
 
	//Listener an den Tree haengen
	treeAdressen.builder.addListener(KontaktAdressenTreeListener);	
}
 
function refreshTree()
{
	//Aktualisieren des Trees
	AdressenTreeDatasource.Refresh(false);
}

Diese Methode ist zwar etwas aufwendiger als die vorhergehende, dafür ist aber gesichert, dass die Daten vollständig geladen wurden.

Wenn die RDF URL zur Laufzeit verändert werden muss (unterschiedliche Parameter zB für Adressen einer anderen Person) ist zu beachten, dass die „alten“ Listener und Datasources vorher vom Tree entfernt werden.

//Listener und Observer entfernen
try
{
	AdressenTreeDatasource.removeXMLSinkObserver(KontaktAdressenTreeSinkObserver);
	treeAdressen.builder.removeListener(KontaktAdressenTreeListener);
}
catch(e)
{}
 
//Alte Datasource entfernen
var oldDatasources = treeAdressen.database.GetDataSources();
while(oldDatasources.hasMoreElements())
{
	treeAdressen.database.RemoveDataSource(oldDatasources.getNext());
}

Wenn die URL geändert wird, sollte auf jeden Fall ein timestamp an die URL angehängt werden. Geschieht dies nicht, kann es vorkommen, dass das RDF bereits im Cache vorhanden ist und nicht wirklich neu geladen wird. In diesem Fall wird der SinkObserver nicht ausgelöst und infolgedessen der Tree auch nicht refresht!

Einträge im Tree markieren

Da nach dem Refresh eines Trees die vorher markierte Zeile nicht automatisch markiert wird, muss dies vom Programm selbst erledigt werden. Dazu holen wir vor dem Refresh die

aktuell markierte Zeile:

var tree = document.getElementById('adressen-tree');
 
var col = tree.columns ? tree.columns["adresse-treecol-adresse_id"] : "adresse-treecol-adresse_id";
 
try
{
	SelectedID=tree.view.getCellText(tree.currentIndex,col);
}
catch(e)
{
	SelectedID=null;
}
 
AdressenTreeDatasource.Refresh(false);

Nachdem der Tree Refresht wurde (didRebuild) selectieren wir die Zeile neu

function TreeSelectEntry()
{
	var tree=document.getElementById('adressen-tree');
	var items = tree.view.rowCount; //Anzahl der Zeilen ermitteln
 
	//In der globalen Variable ist der zu selektierende Eintrag gespeichert
	if(SelectedID!=null)
	{
	   	for(var i=0;i<items;i++)
	   	{
	   		//id der row holen
			col = tree.columns ? tree.columns["adresse-treecol-adresse_id"] : "adresse-treecol-adresse_id";
			adresse_id=tree.view.getCellText(i,col);
 
			if(adresse_id == SelectedID)
			{
				//Zeile markieren
				tree.view.selection.select(i);
				//Sicherstellen, dass die Zeile im sichtbaren Bereich liegt
				tree.treeBoxObject.ensureRowIsVisible(i);
				return true;
			}
	   	}
	}
}