When you work with large in-memory datasources such as email systems, the using interface can manipulate the data efficiently.. Here are the functions and the property available in a res
Trang 1Chapter 10 RDF, RDF Tools, and the Content Model-P4
When you create RDF statements with assertions or work with in-memory datasources, it is often difficult to remember the shape of the graph, which statements exist about which resources, or which objects are attached to which subjects These "getter" methods can help you verify the shape of your graph
10.3.6 nsIRDFRemoteDataSource
The Section 10.3.3 section (earlier in this chapter) showed how to load a datasource from a remote server simply If you want control over that
datasource, you can manage it by using the nsIRDFRemoteDatasource to set
up a remote datasource:
xml =
'@mozilla.org/rdf/datasource;1?name=xml-datasource';
datasource = Components.classes[xml]
createInstance(Components.interfaces.nsIRDFRemoteDa taSource);
datasource.Init('http://books.mozdev.org/file.rdf')
;
datasource.Refresh(false);
In this example, the Init and Refresh methods control the datasource on the server In addition to these methods, you can call the Flush method to
Trang 2flush the data that's been changed and reload, or you can check whether the datasource is loaded by using the loaded property:
if (datasource.loaded) {
// Do something
}
Built-in datasources that implement nsIRDFRemoteDataSource (and other
necessary interfaces) and do their own data handling include:
@mozilla.org/rdf/datasource;1?name=history
@mozilla.org/browser/bookmarks-service;1
@mozilla.org/autocompleteSession;1?type=history
@mozilla.org/browser/global-history;1
@mozilla.org/rdf/datasource;1?name=bookmarks
10.3.7 nsIRDFPurgeableDataSource
Using the nsIRDFPurgeableDatasource interface allows you to delete a
whole section of an existing in-memory datasource in one fell swoop This means that all relatives all statements derived from that node are
removed When you work with large in-memory datasources (such as email systems), the using interface can manipulate the data efficiently The
Sweep( ) method can delete a section that is marked in the datasource
datasource
QueryInterface(Components.interfaces.nsIRDFPurgeabl eDataSource);
Trang 3rootSubject = RDF.GetResource('urn:root');
predicate =
RDF.GetResource('http://books.mozdev.org/rdf#chapte rs');
object = RDF.GetResource('Chapter1');
datasource.Mark(rootSubject,predicate,object,true);
datasource.Sweep( );
In this instance, a statement about a chapter in a book is marked and then removed from the datasource You can also mark more than one node before sweeping
10.3.8 nsIRDFNode, nsIRDFResource, and nsIRDFLiteral
These types of objects come from only a few different places Here are all the functions that can return the resource of a literal:
nsIRDFService.GetResource
nsIRDFService.GetAnonymousResource
nsIRDFService.GetLiteral
nsIRDFDataSource.GetSource
nsIRDFDataSource.GetTarget
nsIRDFNode is the parent of nsIRDFResource and nsIRDFLiteral It is not
used often because it's sole function is to test equality:
isEqual = resource1.EqualsNode(resource2);
The other two interfaces inherit this function automatically EqualsNode tests the equivalency of two resources, which can be useful when you try to
Trang 4put together different statements (e.g., "Eric wrote a book" and "[This] book
is about XML") and want to verify that a resource like "book" is the same in both cases
10.3.8.1 nsIRDFResource
Like nsIRDFNode, nsIRDFResource is a minimalist interface Here are the functions and the property available in a resource from the nsIRDFResource
interface:
resource = RDF.GetAnonymousResource( );
// get the resource value, something like
'rdf:#$44RG7'
resourceIdentifierString = resource.Value;
// compare the resource to an identifier
isTrue =
resourceEqualsString(resourceIdentifierString);
// Give the resource a real name
resource.Init('Eric');
10.3.8.2 nsIRDFLiteral
A literal's value can be read but not written To change the value of a literal, make a new literal and set it properly:
aValue = literal.Value;
Note that aValue could be a string or an integer in this case The base type conversion, based on the data's format, is done automatically
10.3.9 nsIRDFContainerUtils
Trang 5This interface facilitates the creation of containers and provides other
container-related functions It provides functions that make and work with a sequence, bag, and alternative (The functions work the same way for all types of containers, so only sequence is covered here.) To create an
instance of nsIRDFContainerUtils, use the following:
containerUtils =
Components.classes['@mozilla.org/rdf/container-utils;1']
getService(Components.interfaces.nsIRDFContainerUti ls);
Once you create an anonymous resource, you can create a sequence from it Then you can test the type of the container and see whether it's empty:
// create an anonymous resource
anonResource = RDF.GetAnonymousResource( );
// create a sequence from that resource
aSequence =
containerUtils.MakeSeq(datasource,anonResource);
// test the resource
// (all of these are true)
isContainer =
containerUtils.isContainer(datasource,anonResource)
;
Trang 6isSequence =
containerUtils.isSequence(datasource,anonResource);
isEmpty =
containerUtils.isEmpty(datasource,anonResource);
Note that the sequence object is not passed into the functions performing the test in the previous example; the resource containing the sequence is passed
in Although aSequence and anonResource are basically the same resource, their data types are different isContainer, isSequence, and isEmpty can be used more easily with other RDF functions when a
resource is used as a parameter:
object =
datasource.GetTarget(subject,predicate,true);
if(RDF.isAnonymousResource(object))
{
isSeq = containerUtils.IsSeq(datasource,object);
}
The RDF container utilities also provide an indexing function indexOf is useful for checking if an element exists in a container resource:
indexNumber =
containerUtils.indexOf(datasource,object,RDF.GetLit eral('Eric'));
if(index != -1)
alert('Eric exists in this container');
Trang 710.3.10 nsIRDFContainer
This interface provides vector-like access to an RDF container's elements.[1]
The nsIRDFContainer interface allows you to add, look up, and remove
elements from a container once you create it
10.3.10.1 Adding an element to a container
You can add an element to a container in two ways You can append it to the end of the list with Append or insert it at a specific place in the container:
newLiteral = RDF.GetLiteral('Ian');
aSequence.AppendElement(newLiteral);
// or
aSequence.InsertElementAt(newLiteral,3,true);
The second attribute in InsertElementAt is where the element should
be placed The third attribute specifies that the list can be reordered This method is useful for working with ordered containers such as sequences If this locking parameter is set to false and an element already exists at that location, then the existing element is overwritten
10.3.10.2 Removing an element from a container
Removing an element from a container works much the same as adding one The difference is that a reordering attribute is included on
RemoveElement If this attribute is set to false, you may have holes in the container, which can create problems when enumerating or indexing
elements within
newLiteral = RDF.GetLiteral('Ian');
aSequence.RemoveElement(newLiteral,true);
Trang 8// or
aSequence.RemoveElementAt(newLiteral,3,true);
If you use the indexOf property of nsIRDFContainer, you can also use GetCount to learn how many elements are in the container The count starts at 0 when the container is initialized:
numberOfElements = aSequence.GetCount( );
Once you have the sequence, the datasource and resource the sequence
resides in can be retrieved In effect, these properties look outward instead of toward the data:
seqDatasource = aSequence.DataSource;
seqResource = aSequence.Resource;
Like many methods in the RDF interfaces, this one allows you to traverse and retrieve any part of the RDF graph
10.3.11 nsIRDFXML Interfaces
The RDF/XML interfaces are covered only briefly here Besides being
abstract and confusing, these interfaces require a lot of error handling to work correctly Fortunately, a library on mozdev.org called JSLib handles RDF file access The JSLib XML library does the dirty work in a friendly manner See the section Section 10.5, later in this chapter, for more
information
10.3.11.1 nsIRDFXMLParser and nsIRDFXMLSink
nsIRDFXML is the raw RDF/XML parser of Mozilla Used by Mozilla, its
main purpose is to parse an RDF file asynchronously as a stream listener Though this subject is beyond the scope of this book, the interface provides
Trang 9something interesting and useful The parseString function allows you
to feed nsIRDFXMLParser a string and have it parse that data as RDF and
put it into a datasource, as Example 10-9 demonstrates
Example 10-9 Parse an RDF/XML string into a datasource
RDF =
Components.classes['@mozilla.org/rdf/rdf-service;1']
getService(Components.interfaces.nsIRDFService);
// Used to create a URI below
ios = Components.classes["@mozilla.org/network/io-service;1"]
getService(Components.interfaces.nsIIOService);
xmlParser = '@mozilla.org/rdf/xml-parser;1';
parser = Components.classes[xmlParser]
createInstance(Components.interfaces.nsIRDFXMLParse r);
uri = ios.newURI("http://books.mozdev.org/rdf#", null);
// Entire RDF File stored in a string
rdfString =
'<rdf:RDF xmlns:rdf=http://www.w3.org/1999/02/22-rdf-syntax-ns#' +
Trang 10'xmlns:b="http://books.mozdev.org/rdf#">' +
'<rdf:Description about="urn:root">' + // Rest of file
parser.parseString(datasource,uri,rdfString);
// Parsed string data now resides in the datasource
The RDF/XML data that was in the string is a part of the datasource and ready for use (just like any other RDF data in a datasource) The uri acts as
a base reference for the RDF in case of relative links
nsIRDFXMLParser uses nsIRDFXMLSink for event handling The interfaces
are totally separate, but behind the scenes, they work together with the
incoming data Example 10-10 shows how a series of events is created in an object and then used to handle parser events
Example 10-10 Setup nsIRDFXMLSink with event handlers
var Observer = {
onBeginLoad: function(aSink)
{
alert("Beginning to load the RDF/XML ");
},
onInterrupt: function(aSink) {},
onResume: function(aSink) {},
onEndLoad: function(aSink)
{
Trang 11doneLoading( ); // A function that does
something with the datasource
},
onError: function(aSink, aStatus, aErrorMsg)
{
alert("Error: " + aErrorMsg);
}
};
Once the event handlers are set up, you can use nsIRDFXMLSink:
sink =
datasource.QueryInterface(Components.interfaces.nsI RDFXMLSink);
sink.addXMLSinkObserver(observer);
The events are then triggered automatically when the datasource is loaded up with data, allowing you to create handlers that manipulate the data as it appears
10.3.11.2 nsIRDFXMLSerializer and nsIRDFXMLSource
These two interfaces are meant to work together nsIRDFXMLSerializer lets
you init a datasource into the xml-serializer module that outputs
RDF However, nsIRDFXMLSource actually contains the Serialize
function Here's how to serialize a datasource into an alert:
serializer = '@mozilla.org/rdf/xml-serializer;1';
s = Components.classes[serializer]
Trang 12createInstance(Components.interfaces.nsIRDFXMLSeria lizer);
s.init(datasource);
output = new Object( );
output.write = new function(buf,count)
{
alert(buf); // Show the serialized syntax
return count;
}
s.QueryInterface(Components.interfaces.nsIRDFXMLSou rce).Serialize(output);
As in the previous example with nsIRDFXMLParser, Example 10-10 does
not use RDF data from a file The serialized data is passed directly to an alert, which then displays the generated RDF
Notes
[1] A vector, for those who don't know, is a flexible and more accessible version of the array data structure
10.4 Template Dynamics
Once you learn how to create templates and modify datasources, the ultimate
in template mastery is to apply datasources to a template dynamically
This process is done through the database property of a XUL element that contains a template The object returned by this property has only two
Trang 13methods, AddDataSource and RemoveDataSource A separate
builder.rebuild function is also available for refreshing the template's display, but you probably won't need it once the template automatically updates itself The addition and removal of a datasource to a <tree>
template is demonstrated here:
tree = document.getElementById('tree-template');
tree.database.AddDataSource(someDatasource);
// tree will now update its display to show
contents
tree.database.RemoveDataSource(someDatasource);
// tree will now be empty
// Optional, use only when tree is not updating for some reason
tree.builder.rebuild( );
You can add and remove any datasource as long as the template actually matches the data inside it Also, multiple datasources can be applied to the same template with no problems, which allows you to aggregate data from different places, such as contact data, work information, and computer
hardware information (e.g., "Eric uses a Compaq with the serial number 1223456-1091 to write his book and he sits on the fourth floor of the Acme Building, which is the Bay Area branch of Acme Enterprises.)
10.4.1 Template Dynamics in XBL
Trang 14Putting templates inside XBL can be a useful organizational scheme Here is
a basic implementation of a widget that creates a list of people based on names listed in an attribute:
<people names="Brian King,Eric Murphy,Ian
Oeschger,Pete Collins,David Boswell"/>
Obviously, the comma is used as the delimiter for this list The constructor element in Example 10-11 uses JavaScript to break up this string
Example 10-11 Binding with in-memory datasource and <listbox>
template
<?xml version="1.0"?>
<bindings xmlns ="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekee per/there.is.only.xul">
<binding id="people">
<implementation>
<constructor>
<![CDATA[
// Read the Names into an Array
names =
document.getAnonymousNodes(this)[0].getAttribute('n ames');
names = new String(names);
namesArray= names.split(',');
Trang 15// Initialize the RDF Service
rdf = Components
.classes['@mozilla.org/rdf/rdf-service;1']
.getService(Components.interfaces.nsIRDFService);
// Initialize a Datasource in Memory
inMemory =
'@mozilla.org/rdf/datasource;1?name=in-memory-datasource';
datasource = Components.classes[inMemory]
createInstance(Components.interfaces.nsIRDFDataSour ce);
// Create the Root Node and an Anonymous Resource to Start With
root = rdf.GetResource('urn:root');
people = rdf.GetAnonymousResource( );
// Insert the People resource into the RDF graph
datasource.Assert
(root,