Rdf/xml embedded license examples


Peter Williams <peter.williams@...>
 

My action item for the week was to explore the potential ways of representing the embedded (non-standard) licenses in the rdf/xml format. I have found two possible approaches: associating the license with the SPDXDoc via a property (in addition to the declared/detected license properties) or just describing the licenses as stand alone resources.

Example A: Licenses attached to the SPDXDoc:

<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>
</SPDXDoc>
</rdf:RDF>

Example B: Licenses not attached to the SPDX doc:

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>
</SPDXDoc>

<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>

<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</rdf:RDF>

Both of these produce reasonable rdf graphs. I slightly prefer the example b (unattached licenses) because it is simpler, requires no additional changes to the spec, and is more analogous with the way standard licenses are handed. It is simpler because it has fewer elements and less nesting which i find easier to read. The working copy of the spec supports example b (at least regarding the licensing bits) today. Finally, standard licenses are not attached to the SPDXDoc, so why should embedded licenses be? I am willing to be convinced otherwise, however.

What do the rest of you think?

Peter
www.openlogic.com


Gary O'Neall
 

I tend to agree that B is a better approach. Only plus for A is that the
non-standard license was identified as part of the analysis. The analysis
is represented by the SPDX doc, so we get the properties that describe who
did the analysis, when and who reviewed it - I think all of these properties
would equally apply to the non-standard licenses.

That being said, if a non-standard license is just text copied from the
package I am not sure all of those properties are needed.

One question: Would there be any problem of the name/nodeID being non-unique
when trying to look across SPDX docs if we went with B?

Gary

-----Original Message-----
From: spdx-tech-bounces@...
[mailto:spdx-tech-bounces@...] On Behalf Of Peter Williams
Sent: Wednesday, December 01, 2010 7:54 PM
To: spdx-tech@...
Subject: Rdf/xml embedded license examples

My action item for the week was to explore the potential ways of
representing the embedded (non-standard) licenses in the rdf/xml format.
I have found two possible approaches: associating the license with the
SPDXDoc via a property (in addition to the declared/detected license
properties) or just describing the licenses as stand alone resources.

Example A: Licenses attached to the SPDXDoc:

<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File
rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>
</SPDXDoc>
</rdf:RDF>

Example B: Licenses not attached to the SPDX doc:

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File
rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>
</SPDXDoc>

<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>

<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</rdf:RDF>

Both of these produce reasonable rdf graphs. I slightly prefer the
example b (unattached licenses) because it is simpler, requires no
additional changes to the spec, and is more analogous with the way
standard licenses are handed. It is simpler because it has fewer
elements and less nesting which i find easier to read. The working copy
of the spec supports example b (at least regarding the licensing bits)
today. Finally, standard licenses are not attached to the SPDXDoc, so
why should embedded licenses be? I am willing to be convinced
otherwise, however.

What do the rest of you think?

Peter
www.openlogic.com

_______________________________________________
Spdx-tech mailing list
Spdx-tech@...
https://fossbazaar.org/mailman/listinfo/spdx-tech


Peter Williams <peter.williams@...>
 

On Sun, Dec 5, 2010 at 6:15 PM, Gary O'Neall <gary@...> wrote:
One question: Would there be any problem of the name/nodeID being non-unique
when trying to look across SPDX docs if we went with B?
No. Rdf parsers ensure the uniqueness of blank nodes. Node ids are
scoped to the document in which they are found. In practice this
means that rdf parsers at add a large random number to node ids when
they are read in. In the example, the uri of FullLicense-1 would be
something like `_:nbn1291605708126lNFullLicense-1` once it was read
into a graph. This is true for both examples.

Peter


PS: The full graph of example b in ntriples is

<http://oss.net/foo-1.0.tar.gz>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#Package> .
<http://oss.net/foo-1.0.tar.gz>
<http://spdx.org/spec#DeclaredName> "Foo 1.0 Source" .
<http://oss.net/foo-1.0.tar.gz>
<http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126lNFullLicense-1 .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#Files>
_:bn1291605708126m .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#File> .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#Type> "source" .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126nNFullLicense-2 .
_:bn1291605708126m
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first>
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h> .
_:bn1291605708126m
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
_:bn1291605708126k <http://spdx.org/spec#AnalysisFor>
<http://oss.net/foo-1.0.tar.gz> .
_:nbn1291605708126lNFullLicense-1
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126lNFullLicense-1
<http://spdx.org/spec#LicenseText> "License 1 terms and conditions" .
_:nbn1291605708126nNFullLicense-2
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126nNFullLicense-2
<http://spdx.org/spec#LicenseText> "License 2 terms and conditions" .


Kate Stewart <kate.stewart@...>
 

Hmm, main concern I have is that the temporary license number used to
represent a non standard license is not a global value, but rather is
localized to a specific package (hence SPDXDoc). For that reason, A
probably makes the most sense to be.

Kate

On Sun, 2010-12-05 at 17:08 -0800, Gary O'Neall wrote:
I tend to agree that B is a better approach. Only plus for A is that the
non-standard license was identified as part of the analysis. The analysis
is represented by the SPDX doc, so we get the properties that describe who
did the analysis, when and who reviewed it - I think all of these properties
would equally apply to the non-standard licenses.

That being said, if a non-standard license is just text copied from the
package I am not sure all of those properties are needed.

One question: Would there be any problem of the name/nodeID being non-unique
when trying to look across SPDX docs if we went with B?

Gary

-----Original Message-----
From: spdx-tech-bounces@...
[mailto:spdx-tech-bounces@...] On Behalf Of Peter Williams
Sent: Wednesday, December 01, 2010 7:54 PM
To: spdx-tech@...
Subject: Rdf/xml embedded license examples

My action item for the week was to explore the potential ways of
representing the embedded (non-standard) licenses in the rdf/xml format.
I have found two possible approaches: associating the license with the
SPDXDoc via a property (in addition to the declared/detected license
properties) or just describing the licenses as stand alone resources.

Example A: Licenses attached to the SPDXDoc:

<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File
rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>

<EmbeddedLicense>
<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</EmbeddedLicense>
</SPDXDoc>
</rdf:RDF>

Example B: Licenses not attached to the SPDX doc:

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://spdx.org/spec#">
<SPDXDoc>
<AnalysisFor>
<Package rdf:about="http://oss.net/foo-1.0.tar.gz">
<DeclaredName>Foo 1.0 Source</DeclaredName>
<DeclaredLicense rdf:nodeID="FullLicense-1"/>
<Files rdf:parseType="Collection">
<File
rdf:about="http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h">
<Type>source</Type>
<DeclaredLicense rdf:nodeID="FullLicense-2"/>
</File>
</Files>
</Package>
</AnalysisFor>
</SPDXDoc>

<License rdf:nodeID="FullLicense-1">
<LicenseText>License 1 terms and conditions</LicenseText>
</License>

<License rdf:nodeID="FullLicense-2">
<LicenseText>License 2 terms and conditions</LicenseText>
</License>
</rdf:RDF>

Both of these produce reasonable rdf graphs. I slightly prefer the
example b (unattached licenses) because it is simpler, requires no
additional changes to the spec, and is more analogous with the way
standard licenses are handed. It is simpler because it has fewer
elements and less nesting which i find easier to read. The working copy
of the spec supports example b (at least regarding the licensing bits)
today. Finally, standard licenses are not attached to the SPDXDoc, so
why should embedded licenses be? I am willing to be convinced
otherwise, however.

What do the rest of you think?

Peter
www.openlogic.com

_______________________________________________
Spdx-tech mailing list
Spdx-tech@...
https://fossbazaar.org/mailman/listinfo/spdx-tech

_______________________________________________
Spdx-tech mailing list
Spdx-tech@...
https://fossbazaar.org/mailman/listinfo/spdx-tech


Peter Williams <peter.williams@...>
 

On Mon, Dec 6, 2010 at 8:45 AM, Kate Stewart <kate.stewart@...> wrote:
Hmm,  main concern I have is that the temporary license number used to
represent a non standard license is not a global value, but rather is
localized to a specific package (hence SPDXDoc).  For that reason, A
probably makes the most sense to be.
The non-standard licenses will be global regardless of which of those
examples we use. Rdf only has one level of storage. Everything ends
up in the graph next to everything else. Basically the only
difference between example a and b is that in a there is an addition
property connecting the spdx document to the non-standard licenses.

Peter

PS: The following is the ntriples version of the graph produced by example a.

_:bn1291605708126ai <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#SPDXDoc> .
<http://oss.net/foo-1.0.tar.gz>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#Package> .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#DeclaredName>
"Foo 1.0 Source" .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126ajNFullLicense-1 .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#Files>
_:bn1291605708126ak .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#File> .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#Type> "source" .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126alNFullLicense-2 .
_:bn1291605708126ak <http://www.w3.org/1999/02/22-rdf-syntax-ns#first>
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h> .
_:bn1291605708126ak <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
_:bn1291605708126ai <http://spdx.org/spec#AnalysisFor>
<http://oss.net/foo-1.0.tar.gz> .
_:nbn1291605708126ajNFullLicense-1
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126ajNFullLicense-1 <http://spdx.org/spec#LicenseText>
"License 1 terms and conditions" .
_:bn1291605708126ai <http://spdx.org/spec#EmbeddedLicense>
_:nbn1291605708126ajNFullLicense-1 .
_:nbn1291605708126alNFullLicense-2
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126alNFullLicense-2 <http://spdx.org/spec#LicenseText>
"License 2 terms and conditions" .
_:bn1291605708126ai <http://spdx.org/spec#EmbeddedLicense>
_:nbn1291605708126alNFullLicense-2 .


Kate Stewart <kate.stewart@...>
 

On Mon, 2010-12-06 at 10:38 -0700, Peter Williams wrote:
On Mon, Dec 6, 2010 at 8:45 AM, Kate Stewart <kate.stewart@...> wrote:
Hmm, main concern I have is that the temporary license number used to
represent a non standard license is not a global value, but rather is
localized to a specific package (hence SPDXDoc). For that reason, A
probably makes the most sense to be.
The non-standard licenses will be global regardless of which of those
examples we use. Rdf only has one level of storage. Everything ends
up in the graph next to everything else. Basically the only
difference between example a and b is that in a there is an addition
property connecting the spdx document to the non-standard licenses.
Are we talking about the same sort of global?

Non standard licenses are only what is found in the package, and should
have no meaning outside of it.

(ie. license 1 in SPDX doc for package a, is quite different from
licene 1 in SPDX doc for package b).

Kate


Peter

PS: The following is the ntriples version of the graph produced by example a.

_:bn1291605708126ai <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#SPDXDoc> .
<http://oss.net/foo-1.0.tar.gz>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#Package> .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#DeclaredName>
"Foo 1.0 Source" .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126ajNFullLicense-1 .
<http://oss.net/foo-1.0.tar.gz> <http://spdx.org/spec#Files>
_:bn1291605708126ak .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#File> .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#Type> "source" .
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h>
<http://spdx.org/spec#DeclaredLicense>
_:nbn1291605708126alNFullLicense-2 .
_:bn1291605708126ak <http://www.w3.org/1999/02/22-rdf-syntax-ns#first>
<http://oss.net/foo-1.0.tar.gz#contrib/iostream2/zstream.h> .
_:bn1291605708126ak <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest>
<http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
_:bn1291605708126ai <http://spdx.org/spec#AnalysisFor>
<http://oss.net/foo-1.0.tar.gz> .
_:nbn1291605708126ajNFullLicense-1
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126ajNFullLicense-1 <http://spdx.org/spec#LicenseText>
"License 1 terms and conditions" .
_:bn1291605708126ai <http://spdx.org/spec#EmbeddedLicense>
_:nbn1291605708126ajNFullLicense-1 .
_:nbn1291605708126alNFullLicense-2
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>
<http://spdx.org/spec#License> .
_:nbn1291605708126alNFullLicense-2 <http://spdx.org/spec#LicenseText>
"License 2 terms and conditions" .
_:bn1291605708126ai <http://spdx.org/spec#EmbeddedLicense>
_:nbn1291605708126alNFullLicense-2 .
_______________________________________________
Spdx-tech mailing list
Spdx-tech@...
https://fossbazaar.org/mailman/listinfo/spdx-tech


Peter Williams <peter.williams@...>
 

On Mon, Dec 6, 2010 at 12:00 PM, Kate Stewart
<kate.stewart@...> wrote:
On Mon, 2010-12-06 at 10:38 -0700, Peter Williams wrote:
On Mon, Dec 6, 2010 at 8:45 AM, Kate Stewart <kate.stewart@...> wrote:
Hmm,  main concern I have is that the temporary license number used to
represent a non standard license is not a global value, but rather is
localized to a specific package (hence SPDXDoc).  For that reason, A
probably makes the most sense to be.
The non-standard licenses will be global regardless of which of those
examples we use.  Rdf only has one level of storage.  Everything ends
up in the graph next to everything else.  Basically the only
difference between example a and b is that in a there is an addition
property connecting the spdx document to the non-standard licenses.
Are we talking about the same sort of global?
I mean that rdf has exactly one address space. Any entity you want to
model goes in that address space. Node id's are a convenience
mechanism to deal with situations like ours. Node ids are references
whose scope is limited to the current file. When a node with a node
id is read in for the file it will be converted into a node in the
global graph address space and assigned a uri. (The uri is usually
based on the limited scope node id plus something unique like a random
or sequence number.)

Non standard licenses are only what is found in the package, and should
have no meaning outside of it.


(ie. license 1 in SPDX doc for package a, is quite different from
licene 1 in SPDX doc for package b).
In the scenario where one reads two spdx files each containing a
non-standard license node with @id='FullLicense-1' into a single graph
they will *not* collide. However, both of the non-standard licenses
will be top level nodes in the graph, each with their own unique uri.
This is true regardless whether the licenses are directly associated
with the spdx doc or not.

Peter


Kate Stewart <kate.stewart@...>
 

On Mon, 2010-12-06 at 12:30 -0700, Peter Williams wrote:
On Mon, Dec 6, 2010 at 12:00 PM, Kate Stewart
<kate.stewart@...> wrote:
Non standard licenses are only what is found in the package, and should
have no meaning outside of it.


(ie. license 1 in SPDX doc for package a, is quite different from
licene 1 in SPDX doc for package b).
In the scenario where one reads two spdx files each containing a
non-standard license node with @id='FullLicense-1' into a single graph
they will *not* collide. However, both of the non-standard licenses
will be top level nodes in the graph, each with their own unique uri.
This is true regardless whether the licenses are directly associated
with the spdx doc or not.
Its licenses being top-level in the graph that feels wrong to me, its
like files in a package being top level as well.

Splitting out licenses for different treatment than files isn't
symmetric.

Kate


Peter Williams <peter.williams@...>
 

On Mon, Dec 6, 2010 at 2:00 PM, Kate Stewart <kate.stewart@...> wrote:
On Mon, 2010-12-06 at 12:30 -0700, Peter Williams wrote:
On Mon, Dec 6, 2010 at 12:00 PM, Kate Stewart
<kate.stewart@...> wrote:
Non standard licenses are only what is found in the package, and should
have no meaning outside of it.


(ie.  license 1 in SPDX doc for package a,   is quite different from
licene 1 in SPDX doc for package b).
In the scenario where one reads two spdx files each containing a
non-standard license node with @id='FullLicense-1' into a single graph
they will *not* collide.  However, both of the non-standard licenses
will be top level nodes in the graph, each with their own unique uri.
This is true regardless whether the licenses are directly associated
with the spdx doc or not.
Its licenses being top-level in the graph that feels wrong to me, its
like files in a package being top level as well.

Splitting out licenses for different treatment than files isn't
symmetric.
Files exist at the top level, too.

From a information engineering point of view it makes a lot of sense.
One might care about files and not the packages to which they belong.
Same for non-standard licenses.

All that doesn't really matter because rdf does not give us any other
options. Everything is either a node or a link and all nodes are at
the top level.

Peter