This repository contains interfaces for, and an implementation of, an API for OWL ontologies along with some sample applications of the API.
The code can be built using the supplied ant
scripts (ant 1.5
required). You will also need to have the optional JUnit
tasks installed for ant.
The build structure is set up so that each module is contained in
its own directory. Modules can be built separately — if module
a
depends on b
then b
will be
built if necessary. Directory owlapi
contains a build
file that relates to the entire release. Each module can be built
independently by running ant
tasks within the appropriate
subdirectory, or the entire project can be built by running ant tasks
within the owlapi
directory.
The following describes the default build structure. The build structure uses included XML fragments to share targets and properties between the various modules. These include:
names.xml
common.xml
modules.xml
ext.xml
If required, locations of the directories and files can be changed by editing the included xml files.
In the following description, we assume that the distribution is in
directory $OWL
.
Each module has its own directory, where source for classes and
tests are kept. For example, for module validation
we
have:
$OWL |- validation |- src | <java source> |- tests <java source for tests>
We will used module validation
throughout this
description when operations relating to a module are being
discussed.
The command:
> ant -projecthelp
can be used to determine the tasks that can be run. A brief summary of the tasks available in module directories is as follows.
compile
jar
compile.test
jar.test
run.test
Tasks available for the owlapi
build are as
follows.
compile
compile.test
run.test
distribution.bin
distribution.src
distribution
clean
clean.all
Compile time dependencies are shown in the figure below
Although applications such as the validation and servlets require an implementation, the particular classes to use are determined at run time.
To build a module, run the compile
task:
> cd $OWL/validation > ant compile
When compiled, generated class files are produced in a directory
$OWL/build/validation/classes
. Generated class files for tests
are produced in $OWL/build/validation/test.classes
:
$OWL |- build |- validation |- classes | <generated java classes> |- test.classes <generated java test classes>
To build a jar, run the jar
task:
> cd $OWL/validation > ant jar
Generated jar files (one for each module) will be built in
directory $OWL/build/owlapi/lib
.
$OWL |- build |- owlapi |- lib |- validation.jar
Javadoc documentation for an individual module can be built using:
> cd $OWL/validation > ant javadoc
This will produce module documention in the
$OWL/build/validation/javadoc
directory.
Documentation for all modules can be built using:
> cd $OWL/owlapi > ant javadoc
This will produce documention in the
$OWL/build/owlapi/javadoc
directory.
A demonstration application consisting of some simple servlets is
included in the servlet
directory. To build the servlet, run
> cd $OWL/servlet > ant war
This will build an OWL.war
in
$OWL/build/servlet/webapps
. This file can then be
deployed in any appropriate servlet engine such as Tomcat or
Jetty. Directory $OWL/servlet/src
contains configuration
information relating to the servlet.
A suite of JUnit tests are included in the distribution.
Some of these (for instance the build tests of the validator and
consistency checkers) make use of the OWL Test Cases. Network
connectivity is (of course) required to run these tests. The resources directory contains a number of
resources relating to testing (such a list of test manifests). The list
of manifests used during testing can be set using the property
tests.manifests
in common.xml
.
Test for each module can be run from the module directory.
> cd $OWL/validation > ant run.test
In this case, test results will appear in directory
$OWL/build/validation/test.results
. Alternatively, the entire test
suite (for all modules) can be run from the owlapi
directory:
> cd $OWL/owlapi > ant run.test
In this case, all results will appear in
$OWL/build/owlapi/test.results
.
We provide a very brief overview here of the interfaces and class in the API — more detail is provided in the Javadoc documentation. The example classes may also help to illustrate how to use the API. We include some simple code fragments — note that these are not intended to be complete and compilable!
The API provides a number of classes and interfaces intended to
represent OWL ontologies, which can be found in package
org.semanticweb.owl.model
. Ontologies consist of Classes,
Properties and Individuals — within an ontology there may be
definitions of these objects, for example statements that two classes
are equal (e.g. have the same extension) or that one class is a
specialization of another.
Interfaces are provided for each of the basic things that we find in an ontology:
OWLClass
OWLObjectProperty
OWLDatatypeProperty
OWLIndividual
These interfaces share some functionality, for example they all
have a URI
that provides an identifier for the
object.
An important point to be aware of is that within the API, these
objects can exist independently of an ontology — in
fact, an object representing a class can appear in many ontologies. In
addition, any assertions made about the class have to be made within
the context of an ontology. Thus it does not make sense to simply ask
"what are the superclasses of class X?". Instead, we must ask
"what are the superclasses of class X in ontology O?". This
is reflected in the accessor methods on, e.g. OWLClass
:
Set getEnumerations(OWLOntology o)
Set getEnumerations(Set
ontologies)
Set getEquivalentClasses(OWLOntology o)
Set getEquivalentClasses(Set ontologies)
Set getSuperClasses(OWLOntology o)
Set getSuperClasses(Set ontologies)
There is often more than one way of saying something in OWL. For
example, in order to represent the fact that all Persons
are Animals
, we may represent this as part of the
"definition" of Person
:
Class(Animal partial) Class(Person partial Animal)
or as a subclass axiom:
Class(Animal partial) Class(Person partial) SubClassOf(Person Animal)
Both have the same semantic effect, but perhaps convey different
modelling intentions. The API preserves this distinction. In the first
case, the information can be represented through the addition of a
superclass to the class Person
(within the context of the
ontology). In the second, the information is represented as an
OWLSubClassAxiom
(again added to the ontology in
question).
Many of the classes and interfaces defined in the API make use of
collections. For example, OWLNaryBooleanDescription
has a
collection of expressions which are (unsurprisingly) the operands to
the operator. The interfaces use the type Set
. This means
that users of the API will have to cast objects as they are extracted
from the lists, e.g. the operands of a conjunction may have to be cast
to OWLDescription
before they can be used. The issue here
is in the trade-off between type-safety (providing a number of
different typed collection classes) and keeping the interfaces small,
easy to understand, maintain and implement.
The Visitor Pattern (See Design Patterns, Gamma et. al. p.331 for a detailed description) is used throughout the API. Use of the Visitor architecture allows us to add application-specific functionality without "tainting" the data structure.
For example, in order to define operations over arbitrary
OWLDescriptions
(e.g. providing some kind of concrete
representation), implement OWLDescriptionVisitor
interface
accordingly. For each concrete class C
in the
OWLDescription
hierarchy, a function
visit( C )
must be provided. The expression can then be
visited using the accept
method.
The code fragment below illustrates how we might use a visitor to process the super classes of a class.
OWLOntology ontology; OWLClass clazz; URI uri; OWLDescriptionVisitor visitor; ... /* Some kind of initialization */ ... try { /* Try and get a particular class */ clazz = ontology.getClass( uri ); /* Find out the supers of the class in the context of the given ontology. */ Set supers = clazz.getSuperClasses( ontology ); /* Iterate over the supers */ for (Iterator it = supers.iterator(); it.hasNext(); ) { /* They should be instances of OWLDescription */ OWLDescription desc = (OWLDescription) it.next(); /* Pass to the visitor for some processing. */ desc.accept( visitor ); } } catch ( Exception e ) { /* Take necessary action */ ... }
The org.semanticweb.owl.model
package decribed above provides
read-only access to the data structures representing an OWL
Ontology. In order to change the ontological structure, e.g. add
classes, define classes or add axioms, we have to use the change
events provided in the org.semanticweb.owl.model.change
package.
In order to enact a change, we must first create an object that
encapsulates that change (providing any necessary arguments), then
pass the change to a ChangeVisitor
that will then perform
the necessary actions. For example, to create a new class and then add
it to an ontology, something like the following is required:
OWLDataFactory factory; OWLOntology ontology; ChangeVisitor visitor; ... /* Ensure that visitor is really a visitor that enacts changes over ontology */ ... try { /* Create a new OWLClass */ OWLClass clazz = factory.getOWLClass( uri ); /* Create a new change object representing the addition of clazz to ontology. The third argument is null signifying that there is no explicit cause for this change. */ AddEntity ae = new AddEntity( ontology, clazz, null ); /* Use the visitor to enact the change */ ae.accept( visitor ); } catch ( Exception e ) { /* Any necessary cleanup */ ... }
The OWL API as described above consists of a number of interface classes providing access to OWL ontologies. Particular concrete implementations can then implement the required functionality, but the applications (such as an editor or a parser) need not know about the particular details of an implementation. However, at some point, we must make a choice about the actual classes used to implement the interfaces. The API ships with a simple "reference" implementation — this section describes how to access this. Note that the reference implementation is a rather simple main-memory based implementation and is not particularly optimised.
The org.semanticweb.owl.util.OWLConnection
class
represents a connection to some implementation provider. An
OWLOntology
is unique with respect to its logical URI
within a connection, and an instance of
OWLConnection
holds a single
org.semanticweb.owl.model.OWLDataFactory
. This data
factory is then responsible for the creation of objects.
The org.semanticweb.owl.util.OWLManager
class provides
a manager for factories of OWL Ontologies
(e.g. OWLConnections
). It is the central point of access
to OWLOntology
implementations and thereby to OWL
Ontologies.
Applications that create new ontology objects (such as parsers)
should do so within the context of a particular connection. For
example, the setConnection( OWLConnection )
method on
org.semanticweb.io.Parser
sets the particular
connection that the parser uses to gain access to an
implementation.
The code sample below shows how we might gain access to an instance of a connection and then use it.
OWLConnection connection = null; try { Map parameters = new HashMap(); /* Set up the implementation class */ parameters.put(OWLManager.OWL_CONNECTION, "org.semanticweb.owl.impl.model.OWLConnectionImpl"); connection = OWLManager.getOWLConnection(parameters); } catch ( OWLException e ) { System.err.println("Could not obtain connection:"); System.err.println( e.getMessage()); System.exit(-1); } /* From this point on, all code is in terms of interfaces and abstract classes */ /* Get the data factory */ OWLDataFactory fact = connection.getDataFactory(); URI uri = new URI("http://example.org/ontologies/onto"); /* Get a new ontology */ OWLOntology ontology = connection.createOWLOntology( uri, uri ); /* Get a change visitor which will enact change events over the ontology */ ChangeVisitor visitor = connection.getChangeVisitor( ontology ); /* Get some new classes. */ OWLClass clazz1 = factory.getOWLClass( new URI("http://example.org/ontologies/onto#class1") ); OWLClass clazz2 = factory.getOWLClass( new URI("http://example.org/ontologies/onto#class2") ); /* Get a change object representng the addition of clazz2 as a super of clazz1 */ OntologyChange oc = new AddSuperClass( ontology, clazz1, clazz2, null ); /* Add it */ oc.accept( visitor );
The OWLManager
class can supply a default
implementation of OWLConnection
. This is determine
dynamically at run time using the following mechanisms:
org.semanticweb.owl.util.OWLConnection
is set, then the
value of this indicates the concrete implementation class that should
be used. META-INF/services/org.semanticweb.owl.util.OWLConnection
.
The contents of this resource should specify the class to use.The OWL API requires Java2. The parser for the abstract syntax requires Java 1.4.2 or later as there appears to be a bug in the 1.4.1 JVM that causes problems with the ANTLR generated parser. As a result, in the current distribution, the basic tests for the abstract OWL parser are not run when the build tests are run.
This code is being made available under the GNU Lesser General Public License (LGPL).
For questions regarding the API, please contact Sean Bechhofer.