Friday, March 07, 2008

How to locally change a remote RDF datasource loaded from a HTTP URL

So I've struggled with this problem a while back and forgot completely about it and struggled again with it. You can't do anything with a nsIRDFRemoteDataSource loaded via HTTP. It is printed as plain as day on this page.

RDF/XML datasources may be loaded from any type of URL. Currently, only those loaded from file URLs (URLs that begin with 'file:') may be modified with the RDF modification APIs. One possible workaround for modifying remote RDF sources is to load the RDF and then add the data into a separate in-memory-datasource.
But of course it doesn't give an example of doing this workaround.

Here is what I came up with based on some snippets I found on the web and put them together to basically take the data source and serialize it to a string and the parse it into an in-memory-datasource.

function getInMemoryDataSource(url) {
var outputStream = {
data: "",
close : function(){},
flush : function(){},
write : function (buffer,count){
this.data += buffer;
return count;
},
writeFrom : function (stream,count){},
isNonBlocking: false
};
var mem = '@mozilla.org/rdf/datasource;1?name=in-memory-datasource';
var datasource = Components.classes[mem].createInstance(Components.interfaces.nsIRDFDataSource);
var ds = LOCAL_RDF.GetDataSourceBlocking(url);
ds.QueryInterface(Components.interfaces.nsIRDFXMLSource);
ds.Serialize(outputStream);
// Used to create a URI below
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var xmlParser = '@mozilla.org/rdf/xml-parser;1';
var parser = Components.classes[xmlParser].
createInstance(Components.interfaces.nsIRDFXMLParser);
var uri = ios.newURI(url, null, null);
// Entire RDF File stored in a string
parser.parseString(datasource,uri,outputStream.data);
return datasource;
}
This will take in the URL you wanted to load and give you back a nsIRDFDataSource that you can now manipulate. If it is attached to a XUL template it will now update the template as it notices changes in the datasource like it should.

It really shouldn't be this hard, but when it works it is nice.