Chapter 10

RDF, RDF Tools, and the Content
Model

Chapter 9 introduced the
Resource Description Framework (RDF) as the basis for building
display data in the interface, where XUL templates take
RDF-based data and transform it into regular widgets. But RDF
is used in many other more subtle ways in Mozilla. In fact, it
is the technology Mozilla uses for much of its own internal
data handling and manipulation.

RDF is, as its name suggests, a framework for integrating
many types of data that go into the browser, including
bookmarks, mail messages, user profiles, IRC channels, new
Mozilla applications, and your collection of sidebar tabs. All
these items are sets of data that RDF represents and
incorporates into the browser consistently. RDF is used
prolifically in Mozilla, which is why this chapter is so
dense.

This chapter introduces RDF, provides some detail about how
Mozilla uses RDF for its own purposes, and describes the RDF
tools that are available on the Mozilla platform. The chapter
includes information on special JavaScript libraries that make
RDF processing much easier, and on the use of RDF in manifests
to represent JAR file contents and cross-platform installation
archives to Mozilla.

Once you understand the concepts in this chapter, you can
make better use of data and metadata in your own application
development.

RDF Basics

RDF has two
parts: the RDF Data Model and the RDF Syntax (or
Grammar). The RDF Data Model is a graph with nodes and arcs,
much like other data graphs. More specifically, it's a
labeled-directed graph. All nodes and arcs have some
type of label (i.e., an identifier) on them, and arcs point
only in one direction.

The RDF Syntax determines how the RDF Data Model is
represented, typically as a special kind of XML. Most XML
specifications define data in a tree-like model, such as XUL
and XBL. But the RDF Data Model cannot be represented in a true
tree-like structure, so the RDF/XML syntax includes properties
that allow you to represent the same data in more than one way:
elements can appear in different orders but mean the same
thing, the same data can be represented as a child element or
as a parent attribute, and data have indirect meanings. The
meaning is not inherent in the structure of the RDF/XML itself;
only the relationships are inherent. Thus, an RDF processor
must make sense of the represented RDF data. Fortunately, an
excellent RDF processor is integrated into Mozilla.

RDF Data Model

Three
different types of RDF objects
are the basis for all other RDF concepts: resources,
properties, and statements. Resources
are any type of data described by
RDF. Just as an English sentence is comprised of subjects and
objects, the resources described in RDF are typically subjects
and objects of RDF statements. Consider this example:

Eric wrote a book.

Eric is the subject of this statement, and would
probably be an RDF resource in an RDF statement. A book,
the object, might also be a resource because it represents
something about which we might want to say more in RDF-for
example, the book is a computer book or the book sells for
twenty dollars. A property is a
characteristic of a resource and might have a relationship to
other resources. In the example, the book was written by Eric.
In the context of RDF, wrote is a property of the
Eric resource. An RDF statement is a resource, a
property, and another resource grouped together. Our example,
made into an RDF statement, might look like this:

(Eric) wrote (a book)

Joining RDF statements makes an entire RDF graph.

NOTE

We are describing the RDF data model here, not the RDF
syntax. The RDF syntax uses XML to describe RDF statements
and the relationship of resources.

As mentioned in the introduction, the RDF content model is a
labeled-directed graph, which means that all relationships
expressed in the graph are unidirectional, as displayed in Figure 10-1.

Figure 10-1: Simple
labeled-directed graph

A resource can contain either a URI or a literal. The root
resource might have a
URI,
for example, from which all other resources in the graph
descend. The RDF processor continues from the root resource
along its properties to other resources in the graph until it
runs out of properties to traverse. RDF processing terminates
at a literal, which is just what it
sounds like: something that stands only for itself, generally
represented by a string (e.g., "book," if there were no more
information about the book in the graph). A literal resource
contains only non-RDF data. A literal is a terminal point in
the RDF graph.

For a resource to be labeled, it must be addressed through a
universal resource identifier (URI). This address must be a
unique string that designates what the resource is. In
practice, most resources don't have identifiers because they
are not nodes on the RDF graph that are meant to be accessed
through a URI. Figure 10-2 is a modified
version of Figure 10-1 that shows
Eric as a resource identifier and book as a
literal.

Figure 10-2: Resource to
literal relationship

Resources can have any number of properties, which
themselves differ. In Figure 10-2,
wrote is a property of Eric. However, resources
can also have multiple properties, as shown in Figure 10-3.

Figure 10-3: RDF Graph with
five nodes

The RDF graph in Figure 10-3 has five
nodes, two resources, and three literals. If this graph were
represented in XML, it would probably have three different XML
namespaces inside of it: RDF/XML, a book XML
specification, and a computer XML specification. In
English, the graph in Figure 10-3 might be
expressed as follows:

Eric wrote a book of unknown information. Eric's computer is
700 MHz and has an Athlon CPU.

Note that if Eric wrote a poem and a book, it would be
possible to have two wrote properties for the same
resource. Using the same property to point to separate
resources is confusing, however. Instead, RDF containers (see
the section "RDF containers," later in
this chapter) are the best way to organize data that would
otherwise need a single property to branch in this way.

When you use namespaces, the graph looks much different, as
shown in Figure 10-4.

Figure 10-4: Namespaces
applied to Figure 10-3

NOTE

The resource identifier is often displayed in a URL format
too, but it shouldn't use the same namespace URL as the
RDF/XML file. The URL typically tries to describe a unique
object, such as http://my.jar-of-flies.com.

RDF triples: subject, predicate, and
object

A triple
is a type of RDF statement. While an RDF statement can be a
loose collection of resources, properties, and literals, a
triple typically defines a tighter relationship between such
elements.

The first part of a triple is the subject. This part
is the resource described by the triple. The second part of the
triple is the predicate. This part is a subject's
property, a thing that joins it with something else. The third
part is the object, which is either a resource or a
literal.

RDF triples are significant because their stricter semantics
guarantee the relationship between parts. A triple is a more
formal version of the RDF statement, which is used more
broadly. In Figure 10-4, all statements
are formally subject > predicate > object, so those
statements are triples.

RDF data model terminology

When
reading RDF
specifications, documentation, examples, and other related
material on the Internet, you can encounter a dizzying array of
terms that mean the same thing. Table 10-1
should help clarify these different terms. The italicized
versions of the synonyms all do not technically mean the same
thing, but are loose synonyms whose meanings depend on the
context in which they are used.

Table 10-1: Synonyms in
RDF

Common term

Synonyms

Resource

Subject, object

Resource identifier

Name, (resource) URI, ID, identifier, URL, label

Properties

Attributes

Statement

Triple, tuple, binding, assertion

Subject

Source, resource, node, root

Predicate

Arc, (statement) URI, property, atom

Object

Value, resource, node, literal

RDF Syntax

Mozilla
uses XML to represent RDF
data. In 1999, the W3C defined the RDF/XML specification syntax
to make it the most common way RDF is used. The RDF/XML format
is sometimes called the RDF serialization syntax because it
allows RDF models to be sent easily from one computer
application to another in a common XML format.

When an application reads an RDF file, the Mozilla RDF
processor builds a graphical interpretation in-memory. In this
section, you learn how to build an RDF file from scratch and
see what the graph looks like after running through Mozilla's
RDF processor.

NOTE

RDF:RDF is a common namespace representation of
RDF/XML data and is the one most frequently used in Mozilla
files. However, it can be hard to read, so this chapter uses
rdf:RDF. The W3C also used rdf:RDF in the
RDF recommendation document.

Examining a simple RDF file

We begin
with an example of an RDF file whose basic layout and simple syntax can be a
model for the more advanced data introduced later. The RDF file shown in Example
10-1 is a list of three types of "flies", with the context of those "flies"
inside a "jar." Example 10-1 also contains a namespace
that defines these types of flies and shows the rdf and fly
XML intertwined.

<rdf:Description> is the tag used to outline a
resource. Example 10-1 shows how the
about attribute references the resource identifier and
makes this resource unique in the document. Two resources
cannot have the same about value in a document, just
as tags cannot share an id in an XML document. Both
attributes guarantee the unique nature of each element and
relationship.

<rdf:Description about="urn:root">
<fly:types>
<rdf:Bag>

urn:root, is the subject shown in the previous code snippet. It is
also a resource definition and defines only what flies are inside of the statement.
The predicate, which addresses a property in the resource, is defined by the
tag <types> (of the http://xfly.mozdev.org/fly-rdf#
namespace).

The final part of the statement, the object, is the actual data of the predicate
and a container of type bag. The container is an RDF resource that "holds",
or points to, a collection of other resources. In the next section, container
types are discussed in depth. Figure 10-5 illustrates how
the triple originates from the root subject and includes the container object.

Figure 10-5: The first
statement of the graph, with labeled parts

In this case, an RDF statement is extracted from the
example, but no useful data is reached. Little can be done with
an empty RDF container, and two more steps are needed to reach
literals that contain names of the flies.

RDF containers

Containers
are a list of resources or
literals. They are a form of RDF
resource. There are
three different container types: bag,
sequence, and alternative. Bag is an
unordered list of items, whereas sequence is an
ordered list of items. They both allow duplicate values.
Alternative is a list of values that could replace a
particular property in a resource. Sequence is the
most popular container for use in Mozilla applications because
it frequently uses ordered lists of data. A container's
graphical definition is an entire separate statement about its
type and the items it contains. In Figure
10-6, you can see the type of the container defined in the
RDF statement with the property rdf:type. The
remaining properties are the container's items.

Figure 10-6: The second
statement of the graph, with labeled parts

Once the container is defined, you can examine its
collection of elements. At this point in the RDF code, direct
comparisons can again be made from the code to the graph:

<rdf:Bag>
<rdf:li>
<rdf:Description ...

Here, the <rdf:li>
tag is similar to the <li> tag in HTML, which
stands for "list item." Moving from code to graph, the new
representation is shown in Figure
10-6.

In Figure 10-6, the subject is the
instance of the container. This statement does not begin from
rdf:Bag because that resource is only a type
definition. The actual items in the container originate from
the instance created in memory by any RDF processor, including
Mozilla's.

NOTE

Mozilla's RDF processor fills in the rdf:*(1) of
the resource identifier in Figure 10-6
with a hashed value. The same is true for the container's
resource identifier. The actual values come out as something
like rdf:#$0mhkm1, though the values change each
time the RDF document is loaded.

Objects inside of the container have
properties identified
automatically as rdf:_1, rdf:_2, etc., as
defined by the RDF model specification. However, RDF
applications such as Mozilla may use different identifiers to
differentiate list objects.

Literals

The final
statement in Example 10-1 allows the
predicate to reach the text data, the literal "horse" shown in
Figure 10-7. Note that the about
reference on the Description is fictitious RDF, but it
demonstrates the difference between a resource and a
literal.

<rdf:Description about="rdf:*(1)" fly:name="Horse"/>

Figure 10-7: The third
statement of the graph, with labeled parts

The previous RDF
code for the literal is
syntactic shorthand. Using this type of shortcut can make RDF
much easier to read. The previous code snippet is the same as
the longer and more cumbersome one shown here:

The shorthand version of this statement can be useful when
you have a lot of data or when you want to use one syntax to
show all relationships in the graph.

The RDF syntax and RDF graphs

Figure 10-8 shows the entire RDF graph
for the RDF file in Example 10-1. This
graph was compiled by combining the concepts you've seen in
Figures 10-5 through 10-7.

As you can see, the statements fit together quite nicely.
Four resources originate from the container, and one is the
container type definition. The other two properties are
numbered according to their order in the RDF file.

Figure 10-8: The full
graph

Building an RDF File from Scratch

Now that
you understand the basic
principles of a simple RDF file, this section steps through the
creation of an RDF file from information found in regular
text:

There is a jar with the name urn:root. Inside of it there are
two types of flies listed as House and Horse. There are three
Horse flies. The Face Fly, coded in green, is officially
identified as "musca autumnalis". The Stable Fly, coded in
black, has the identification "stomoxys_calcitrans." The
red-coded Horn Fly, located in Kansas, is identified as
"haematobia_irritans." There are also three house flies.
"musca_domestica," coded in brown, has the name "Common House
Fly." A gray fly named "Carrion Fly" has the ID "sarcophagid"
and is found globally. Finally, The "Office Fly," coded with
white, is prevalent in the Bay Area.

You can use the techniques described here to model the data
you want in your application: spreadsheet-like rosters of
people, family trees, or catalogs of books or other items.

Identify namespaces

The new
RDF file will
have three namespaces including the RDF namespace. The result
is two different data types that are connected in an RDF graph.
For the sake of the example, one namespace is not in the
standard URL format. Here is how the RDF file namespaces are
set up:

Root resource

This file's
root resource is an
urn:root, which is the conventional name for root
nodes in Mozilla's RDF files. When rendering RDF files,
defining a root node for processing the document can be
useful-especially when building templates. This root node can
be entered as the first item in the file:

Root sequence

Next, a
generic tag needs to be
used to specify a sequence of "fly" data. As in Example 10-2, <fly:list> is used as
a list of fly types. This tag is a generic name because of the
way XUL templates process lists of RDF data. If a list of data
has sublists, as in the following examples, then they must use
the same tag name to recurse correctly for the data they
contain.

Example 10-2 represents all the
information given in the first paragraph of the text example:
"There is a jar set up with the name urn:root. Inside of
it there are two types of flies, listed as House and
Horse."

An RDF sequence resides with its list of resources inside
<fly:list>. Here, shorthand RDF specifies a
label with the fly:label attribute. The ID
attribute within this sequence is actually a pointer to the
main definition of the resource described by an about
attribute of the same value. The about attribute
includes a # in its identifier, much like HTML anchors
use <a href="#frag"> to refer to <a
name="frag">. For example, ID="Horse" points
to about="#Horse" elsewhere in the file, allowing you
to add to the description of any element with new properties
and resources.

Secondary sequences and literals

The
Horse and
House resources need to be defined next. Example 10-3 shows the creation of Horse
from the second paragraph. The process for creating
House is almost identical.

Here the shorthand RDF definition continues to use only the
attributes. Again, a <fly:list> is defined and
the items inside it are listed. The listed values have multiple
attribute values, all of which are RDF literals. In longhand
with RDF showing all literals, the last item would be written
out as follows:

Example 10-4 shows the RDF data used in
several template examples in Chapter
9. Example 9-4 includes the
10-4.rdf datasource, as do many of those templates. You
can copy the data out of Example 10-4 and
into a file of the same name to use as a datasource.

The Mozilla Content Model

One theme
of
this book-and a general goal of the Mozilla development
environment-is that developers can create real applications
using many of the same technologies they use to create a web
page. The Gecko rendering engine, sitting at the heart of
Mozilla and happily rendering web content, XML files, XUL
interfaces, and whatever else they can support, is what makes
this type of development possible. But how does Gecko know what
to render and how? How can RDF data be handed over so that
Gecko knows how to draw it?

When a browser uses the same engine to draw everything-its
own interface as well as the various kinds of content it
supports-that engine treats everything as content. Gecko needs
a way to understand all the various parts of the Mozilla
browser itself-such as the sidebar, the toolbars, and the mail
folders and mail messages-as resources it can render and
display in the Mozilla chrome. This approach to the Mozilla
application interface is called the content model.

In Mozilla's content model, XUL documents and other
interface resources are transformed into RDF when they are
read. Each chunk of content is represented as a separate RDF
datasource (see the next section, "Datasources," for more information) and is then
fed to the XUL Content Builder and rendered as the actual bits
on the screen, as Figure 10-9 shows.

Figure 10-9: Diagram of
Mozilla's content model

As you can see in Figure 10-9, the
content model can be complex. The XUL documents in Figure 10-9 are files such as
navigator.xul, which defines the main browser window's
basic layout; the RDF documents include files like
help-toc.rdf, which defines the Mozilla Help viewer's
table of contents. The list of mail folders and accounts shown
in Example 10-5 are part of the built-in
data that Mozilla renders into browser content.

Whatever the source, the content model gets everything
processed in-memory as RDF so that any data can be combined and
formatted into XUL or other interface code. All sources of RDF
data are called datasources.

Datasources

A datasource
is a collection of
related, typically homogenous, RDF statements. A datasource may
be a single RDF file like localstore.rdf, a combination
of files, or RDF structures that exist only in memory (as
discussed later).

In Mozilla, datasources represent the messages in your email
inbox, your bookmarks, the packages you installed, your browser
history, and other sets of data. Datasources can be combined
easily (or "composed," which is where the term "composite
datasource" comes from).

A datasource example: mailboxes

Several
datasources
describe all the folders and messages in Mozilla's email. A
root datasource called msgaccounts describes which
mail servers and accounts are present. Separate datasources
then represent each account separately. These datasources are
composed to create the entire email storage system. The higher
levels of this content structure look like Example 10-5.

Each direct child of the root msgaccounts:/ is a mail
server. This portion of the graph shows two Mozilla email
accounts that are the primary children: imap://oeschger@imap.netscape.com
and mailbox://oeschger@pop.netscape.com.
These two accounts are entirely different datasources that can
exist on their own. The content model for email actually
extends much lower than what is represented in this outline. It
uses RDF to represent the data all the way into the actual
message lists.

Types of datasources

As you
may have already
inferred, email accounts are not actually RDF files. Mozilla
provides a custom RDF map of all email accounts and messages
and the content model represents the accounts and their
relationships to one another as RDF so they can be integrated
and rendered properly. The interface to this custom mail RDF
map makes it possible to display a list of messages and
mailboxes in a <tree> template.

Another example of a datasource, the
in-memory-datasource, doesn't come from an actual RDF
file. When an in-memory datasource is created, it doesn't
contain data. However, data can be inserted into it and stored
in memory until the datasource is destroyed. In-memory
datasources frequently represent ephemeral data like search
results. Other basic datasource types are described in Table 10-2.

Table 10-2: Types of
datasources

all-packages.rdfin the chromedirectory, which
keeps track packages installed in Mozilla) are local
datasources.assertions, statements that build an
in-memory data model by adding resources, properties, and value
to those.filesystemdatasource and a
historydatasource.

Type

Description

Local datasource

A local datasource is an RDF graph contained in an RDF/XML file on a local
disk. All RDF files in the chrome directory (e.g., all-packages.rdf
in the chrome directory, which keeps track of packages installed
in Mozilla) are local datasources.

Remote datasource

RDF can be accessed locally or remotely. A remote
datasource is an RDF/XML file stored on a server and
accessed with a URL.

In-memory datasource

An in-memory datasource exists only in memory during a Mozilla session.
In-memory datasources are built with assertions, statements that
build an in-memory data model by adding resources, add properties, and value
those.

Built-in datasource

These unique, prefabricated datasources represent something used often
in Mozilla, such as a built-in filesystem datasource and a history
datasource.

Composite datasource

A composite datasource may be a combination of any of
the datasources previously listed. RDF allows you to merge
different graphs.

RDF Components and Interfaces

Once you are comfortable using XUL templates to display RDF
data (see Chapter 9), you should
explore the various ways to create and change that data. In
Mozilla, data is generally RDF, since all data in Mozilla is
either represented formally in RDF or passed through the
RDF-based content model for display. Use the tools described in
this section to manipulate RDF and the data it represents.

Mozilla has a great set of interfaces for creating,
manipulating, and managing RDF, and it also provides ready-made
RDF components that represent datasources used in Mozilla.
Think of RDF interfaces as ways to manipulate RDF directly and
of RDF components as sets of the interfaces already associated
with a particular kind of data, such as bookmarks. Interfaces
tend to deal with the RDF model itself, without regard to the
kinds of data being handled, while RDF components give you
control over specific Mozilla data. See the next two sections
for more information on RDF interfaces and components.

What Is an RDF Component?

An RDF
component may implement
any number of the general RDF interfaces described here, in
addition to special interfaces for accessing and controlling
the data the datasource represents. For example,
@mozilla.org/rdf/data-source;1?name=internetsearch is
an RDF component used to control Mozilla's internet searching
facility. In Mozilla, a component can act as a library of code
specific to a given set of data or domain. The
internetsearch
component is instantiated and used to recall text entered in a
previous search:

This RDF component implements an interface called
nsIInternetSearchService, which is selected from the
component and used to call the RememberLastSearchText
method. Although you can also use the getService
method to get one of a component's RDF interfaces (e.g., by
using
getService(Components.interfaces.nsIRDFDataSource)),
doing so is seldom necessary in practice. RDF components are
tailored to the datasources they represent and usually provide
all the access you need to access that data directly. Example 10-6 lists RDF components in Mozilla.

From this list, components used often in the Mozilla source
code include bookmarks, history, mail and news folders, and
address books.

Special URIs

Mozilla's built-in datasource
components have special URIs
for access. Here is the format used to determine the URI from
the component reference:

Component:

@mozilla.org/rdf/datasource;1?name=SomeName

Datasource URI:

rdf:SomeName

The URI, such as rdf:someName, is also accessible as a datasource
property:

foo-ds.URI

What Are RDF Interfaces?

RDF interfaces
are interfaces in Mozilla
designed to manipulate RDF structures and data. They typically
deal with RDF generally, rather than specific sets of data (as
in the case of components). A common use for an RDF interface
in JavaScript, shown in Example 10-7, is
to use nsIRDFService to retrieve or assert the root node
of an RDF datasource.

Example 10-7: Creating a root
node

// get the nsIRDFService interface and assign it to RDF
RDF = Components.classes.["@mozilla.org/rdf/rdf-service;1"].
getService(Components.interfaces.nsIRDFService);
// call the GetResource method from the interface
rootResource = RDF.GetResource('urn:root');

Like all Mozilla interfaces, RDF interfaces (shown in Table 10-3) are defined in IDL and can be
accessed through XPCOM. The examples in this section use
JavaScript and XPConnect to access the components for
simplicity, but you can also use these interfaces with C++, as
they are often in the actual Mozilla source code. Most
interfaces deal with datasources, which drive the use of RDF in
Mozilla.

Table 10-3: Mozilla's
built-in RDF interfaces

nsIRDFService.

RDF interface

Description

nsIRDFService

Mostly used for retrieving datasources, resources, and literals. It also
registers and unregisters datasources and resources.

nsIRDFCompositeDataSource

Allows the addition and removal of a datasource from a
composite datasource (which may be empty).

nsIRDFDataSource, nsIRDFPurgeableDataSource,
nsIRDFRemoteDataSource

Mostly used for adding, removing, and changing triples
in a datasource. It provides the means to change the
graph.

nsIRDFNode, nsIRDFResource, nsIRDFLiteral

Provide an equality function. Values for resources and literals can be
retrieved. Objects of these types are retrieved from nsIRDFService.

nsIRDFContainer

Provides vector-like access to an RDF container's
elements.

nsIRDFContainerUtils

Provides container creation and other container-related
functions.

nsIRDFObserver

Fires events when data is changed in a datasource.

nsIRDFXMLParser, nsIRDFXMLSerializer, nsIRDFXMLSink,
nsIRDFXMLSource

Used for working with RDF/XML. Functions are provided
for parsing files and serializing content.

The sheer variety of RDF interfaces may seem overwhelming,
but all interfaces serve different purposes and are often used
in conjunction with one another. In your particular application
space, you may find yourself using some subsets of these
interfaces constantly and others not at all. This section
describes some of the most commonly used functions. You can
look up all of interfaces in their entirety
at http://lxr.mozilla.org/seamonkey/source/rdf/base/idl/.

nsIRDFService

If you
will do any sort of
RDF processing, you need to use the nsIRDFService
interface. It provides the basics for working with datasources,
resources, and literals, and is useful when you process RDF
data. nsIRDFService can be initialized by using the
getService method of the rdf-service
class:

Once the service is available, it's ready to go to work.
Even though no datasource is created yet (in this particular
example), the RDF service can still get resources and literals,
as shown in the next section.

Getting a resource

Once a resource
is created
(e.g., with the identifier urn:root in Example 10-7), it needs to be added to a
datasource:

rootResource = RDF.GetResource('urn:root');

When a resource is already registered under the given
identifier (see "Registering and unregistering
datasources," later in this chapter for more information
about RDF registration), then
GetResource returns
that resource.

Getting an anonymous resource

Anonymous resources are resources with no resource
identifier. Here is the creation of a new anonymous resource
and a test of its anonymity:

anonResource = RDF.GetAnonymousResource( );
// This would be true. Checking is not necessary, just here for example.
isAnon = RDF.isAnonymousResource(anonResource);

Typically, these resources are turned into containers, as
shown in the next section. Anonymous resources exist when names
are not needed and a simple reference to that resource is all
that is required.

Getting a literal

The GetLiteral
function returns
the given name in the format of a literal, which you can then
use to assert into an RDF graph as a resource.

myName = RDF.GetLiteral('Eric');

Variations on this function are GetIntLiteral and
GetDateLiteral.

Registering and unregistering
datasources

If you create
a Mozilla application
that uses the same datasource or RDF resources in different
ways, you may want to register the datasource with Mozilla.
When you register a datasource, you register it as a component
in Mozilla (see "Component Manager"
in Chapter 8 for more information on Mozilla's component
model), which means it can be accessed and used as easily as
any other XPCOM component, and from anywhere in Mozilla.

To register a datasource, call the
RegisterDatasource
method of the RDF Service. In this example, the datasource
already exists and is assigned to a variable named
myDatasource:

RDF.RegisterDataSource(myDatasource, false);

In this case, myDatasource is the datasource name,
and the false parameter specifies that this datasource
is not replacing a datasource with the same name. Once a
datasource is registered with the component manager in this
way, it can be retrieved by name and associated with another
instance:

secondDatasource = anotherRDF.GetDataSource("My Datasource");

To unregister a datasource from the RDF Service, pass the
datasource into the UnRegisterDataSource function:

RDF.UnRegisterDataSource(myDatasource);

Once it's unregistered, a datasource is no longer available
to other instances of the RDF Service. Registered resources
work the same way as datasources in the RDF Service: if a
resource is registered with the RDF Service, then it is
available in every instance of RDF Service. To get two
different instances of the same registered datasource and
unregister its use:

If you register resources and datasources, be sure to use
the overwrite Boolean variable on
RegisterDataSource and RegisterResource to
avoid overwriting existing datasources.

Getting a remote datasource

Finally, nsIRDFService provides
a useful method that
loads a datasource from a remote server, which is a process
that occurs asynchronously. Compared to forthcoming discussions
about datasource loading, GetDataSource is a real
shortcut:

Remember that RDF files requested in this way must be set
with the text/rdf MIME type on the web server to load
properly.

nsIRDFCompositeDataSource

When you work
with
multiple datasources, you can make things easier by grouping
them, which nsIRDFCompositeDataSource allows you to do.
This functionality aggregates data in a number of Mozilla's
applications. To get this interface, invoke:

Once you have the interface, adding and removing datasources
from the composite is easy. You can also enumerate the
datasources by using the getNext method. Example 10-8 demonstrates how to add, remove, and
cycle through datasources.

In Example 10-8,
allDataSources is an nsISimpleEnumerator
returned by the GetDataSources method on the composite
datasource. datasource1 is removed from the composite,
and then the remaining datasources are cycled through. This
step provides a way to iterate through a collection of
datasources. nsIRDFCompositeDatasource also inherits the
many functions of nsIRDFDataSource; refer to the section
"nsIRDFDataSource" for more
information.

nsIRDFDataSource

The
nsIRDFDataSource interface is large, with twenty functions
and one attribute (URI), so it's one of the most
common interfaces used to manipulate RDF data.
nsIRDFDataSource contains all the components in Example 10-6 with "datasource" in their contract
IDs, along with other common components:

The nsIRDFDataSource interface is meant to handle
some of the core interaction with the datasource. APIs such as
URI, GetTarget, Assert, and
Change are helpful for working on the RDF graph
itself. For example, the
@mozilla.org/rdf/datasource;1?name=in-memory-datasource
RDF component demonstrates the use of the
nsIRDFDataSource interface. When this component is
created, it's a blank datasource in memory, into which objects
are inserted, changed, and removed. You can access the
nsIRDFDataSource interface from the RDF component by
first constructing an RDF graph in the in-memory
datasource:

The main purpose of the nsIRDFDatasource interface is
to work with RDF triples inside a datasource, allowing you to
change that datasource's RDF graph.

Assertion and removal

Recall from the
"RDF triples: subject, predicate, and object"
section, earlier in this chapter, that triples are RDF
statements in which the relationship between the subject,
predicate, and object is more strictly defined. In the
interface code, a triple's elements are all typically defined
as resources rather than plain URIs, which means they can be
asserted into a datasource in the particular sequence that
makes them meaningful as parts of a triple:

Once you assert the statement's elements into the datasource
in this way, the datasource contains the triple. The
truth value parameter in the last slot indicates that
the given node is "locked" and thus cannot be overwritten.

Removing a triple from the datasource is as easy as adding
it. If you try to remove a triple that doesn't exist, your
request is ignored and no error messages are raised. To
unassert a triple in the datasource, use:

HasAssertion

This next example checks
if the previous statement still exists in the datasource.

datasource.HasAssertion(newSubject,predicate,object,true);

This function is useful when you create new statements and
resources and want to make sure you are not overwriting
pre-existing resources.

GetTarget

The GetTarget method
returns the resource's property value (i.e., the object). Given
the RDF statement "(Eric) wrote (a book)," for example, the
GetTarget method would input "Eric" and "wrote" and
get back the object "a book." Once again, the example code is
based on the previous examples:

object = datasource.GetTarget(newSubject,predicate,true);
objects = datasource.GetTargets(rootSubject,predicate,true);
// objects is an nsIEnumeration of the object and its properties

In addition to GetTarget, as seen above, a
GetTargets function returns an object and its
properties in an enumeration. This function can be very handy
for quick access to resources with fewer function calls.

GetSource

GetSource is the
inverse of GetTarget. Whereas GetTarget
returns an object, GetSource returns the subject
attached to an object. Given the RDF statement "(Eric) wrote (a
book)" again, in other words, the GetSource method
would input "wrote" and "a book" and get back the statement
subject "Eric."

subject = datasource.GetSource(object,predicate,true);
subjects = datasource.GetSources(object,predicate,true);
// subjects is an nsIEnumeration of the subject and its properties

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.

nsIRDFRemoteDataSource

The
"nsIRDFService" 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:

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 flush
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:

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.

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 put 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.

nsIRDFResource

Like
nsIRDFNode,
nsIRDFResource is a minimalist interface. Here are the
functions and the property available in a resource from the
nsIRDFResource interface:

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.

nsIRDFContainerUtils

This 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:

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:

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.

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.

Like many methods in the RDF interfaces, this one allows you
to traverse and retrieve any part of the RDF graph.

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 "JSLib RDF
Files," later in this chapter, for more information.

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 something
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.

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-10Setup nsIRDFXMLSink with event handlers

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.

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:

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.

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 methods, 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.)

Template Dynamics in XBL

Putting 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:

In Example 10-11, everything you need
to display a datasource dynamically is present. The only
difference between this dynamically generated version and a
static RDF-based template is the
datasources="rdf:null", which specifies that the
template does not refer to an actual datasource. Data that is
edited, rearranged, or changed in a different way is often
displayed dynamically in the UI with templates in this
manner.

JSLib RDF Files

Working
with actual RDF files is not easy. However, JSLib (http://jslib.mozdev.org) provides an RDF
file library that can help you develop an RDF-based application. The library
provides many types of error checking, as well as a friendly abstraction away
from the RDF/XML interfaces of Mozilla (see "nsIRDFXML Interfaces,"
later in this chapter). Example 10-12 shows some common uses of the RDFFile
class in JSLib. This functionality can be used in situations in which you have
data in RDF that you want to pull out "manually" and use piece by piece (rather
than as a whole datasource in a template).

This example contains a datasource that represents a
collection of flies. These flies are built up dynamically with
JavaScript objects from the RDF library, which represent the
datasource itself (gRDF = new RDFFile), methods that
view and update the data
(if(gRDF.getAttribute(tempItem,'name')==name), and
utilities that make work with RDF files easier (path =
fileUtils.chrome_to_path(rdfFileURL)).

Example 10-13 initializes and updates a
file after it changes. Example 10-13Initialization

In Example 10-13, the file URL is set
to an RDF file in the chrome area. Note that both a
<tree> and a <listbox>, which
display the same data in different ways, will be updated with
the same datasource. The onload function is called
after the main XUL document is loaded. A class called
FileUtils is initialized, which will create a path to
the RDF file. If the file doesn't already exist, JSLib
automatically creates it.

Finally, the RDFFile is created by using the path and a root resource
identifier, and the "xFly" namespace is used for the data references. Example
10-14 shows that the RDF file is ready to have its data added and deleted.

Example 10-14 contains a modified
version of the update function. First, the function
checks to see if a sequence called types is in the RDF
file. If not, it creates one. Next, it appends an item to the
sequence using type:_+(seqLength+1). The same type of
container setup was described in the section "nsIRDFContainer," earlier in this chapter.

The update function then adds the color, quantity,
and "dead" properties of that new item in the sequence. Next,
it ensures that you actually want to add the item to the RDF
file and flushes it out if not. It then recalls the
onload function to update the template display.

These are the basics of using RDFFile. As you can
see, using JSLib for RDF is often much easier than trying to
implement a similar setup on your own. More information about
RDFFile and the other JSLib libraries can
be
found at http://jslib.mozdev.org/.

Manifests

The package
descriptions, generally called
manifests, use RDF to describe new packages and files to
Mozilla. They can be added seamlessly because RDF provides a
platform-like environment that facilitates the installation and
use of new Mozilla software.

All packages, including the ones that come preinstalled with Mozilla (such
as the browser, the MailNews component, and the en-US language pack), have manifests
describing them in terms of their relation to other packages. The manifests
are typically files called contents.rdf, but they may also be called
manifest.rdf. Example 10-15 presents a contents.rdf
file that describes a new skin for Mozilla.

As you can see, the manifest is divided up into sections.
After the preamble, where the XML processing instruction and
the namespace declarations are made, an RDF sequence lists all
the themes defined or supplemented (since you can create a
package updated for only one Mozilla component, such as the
browser) by this package. This section contains only one
RDF:li-the modern theme.

The next section gives more information on the theme, such
as the author, the theme name, and a description. The
chrome:packages structure that completes the manifest
describes the packages to which this theme should be applied.
All major components of the Netscape browser are listed in this
example-including the AIM client that is not a part of
Mozilla-but is skinned by themes such as Modern.

RDF and Dynamic Overlays

Manifests can
also add new
menu items to existing Mozilla menus. When you add a new
package to Mozilla, you should make it accessible from within
the browser application, where users can access it easily. This
is where RDF and dynamic overlays come in.

The RDF you provide in your package makes it possible for
the chrome registry, discussed in Chapter 6, to find, understand, and
register your new files. Packages must be registered if they
are to be skinned, localized, or accessed using the special
tools Mozilla provides (e.g., the chrome URL or XPConnect to
the XPCOM libraries). If you do not register your package by
providing the necessary RDF manifests, it cannot be accessed
except as a disparate collection of files in the browser's main
content window, which is not what you want.

You can add overlays in Mozilla in two ways: import them
explicitly by using an overlay processing instruction at the
top of the XUL file into which items in the overlay file are to
be "composed," or use RDF to register and load overlay files at
runtime. This latter method will be used here to add an "xFly"
item to the Tools menu of the Mozilla suite of
applications.

Example 10-16 shows the contents.rdf manifest format
that alerts Mozilla of the presence of an overlay, its target in the Mozilla
application, and the package of which it is a part.

The manifest in Example 10-16 names the
file xflyOverlay.xul as an overlay. Then it names
tasksOverlay.xul as the base file into which the
contents are placed. In this case, the overlays can overlay
other overlay files arbitrarily. An overlay can define new
content anywhere in the application. Overlays are often
responsible for putting new items in menus. As long as the
target and overlay ids match, any two RDF datasources
are merged. You can try this example by putting a single new
menu item in an overlay structure like the one shown in Example 10-17. Save it as
xflyOverlay.xul in the xfly content subdirectory
and use the manifest information in Example
10-16 as part of the packaging process described in Chapter 6. Example 10-17Overlay for an xFly menu item in the
browser