edu.stanford.smi.protegex.owlx.examples package in the Protégé-OWL
source code. If you have trouble getting started, please post questions on the
protege-owl mailing list.
edu.stanford.smi.protegex.owl.model
package.
plugins/classes.package com.demo.application;
import edu.stanford.smi.protegex.owl.model.OWLModel; import edu.stanford.smi.protegex.owl.model.OWLNamedClass; import edu.stanford.smi.protegex.owl.ProtegeOWL;
public class OWLAPIDemoApplication {
public static void main(String[] args) { OWLModel owlModel = ProtegeOWL.createJenaOWLModel(); owlModel.getNamespaceManager().setDefaultNamespace("http://hello.com#"); OWLNamedClass worldClass = owlModel.createOWLNamedClass("World"); System.out.println("Class URI: " + worldClass.getURI()); } }
model package. These interfaces provide access to the OWL model
and its elements like classes, properties, and individuals. Application
developers should not access the implementation of these interfaces (such
as DefaultRDFIndividual) directly, but only operate on the interfaces.
Using these interfaces you don't have to worry about the internal details
of how
Protégé
stores ontologies. Everything is abstracted into interfaces and
your code should not make any assumptions about the specific implementation.The most important model interface is OWLModel,
which
provides access to the top-level container of the resources in the ontology.
You can use OWLModel to create, query, and delete resources of various
types and then use the objects returned by the OWLModel to do specific
operations. For example, the following snippet creates a new OWLNamedClass
(which corresponds to owl:Class in OWL), and then gets its URI:
OWLModel owlModel = ProtegeOWL.createJenaOWLModel();
OWLNamedClass worldClass = owlModel.createOWLNamedClass("World");
System.out.println("Class URI: " + worldClass.getURI());
Note that the class ProtegeOWL provides a couple of convenient static
methods to create OWLModels, also from existing OWL files. For example,
you can load an existing ontology from the web using
String uri = "http://www.co-ode.org/ontologies/pizza/2007/02/12/pizza.owl";
OWLModel owlModel = ProtegeOWL.createJenaOWLModelFromURI(uri);http://www.owl-ontologies.com/travel.owl#Destination.
However, since URIs are long and often inconvenient to handle, the primary
access and identification
mechanism for ontological
resources
in the
Protégé-OWL
API is their
name. A name is a short form consisting of local name and an optional
prefix. Prefixes are typically defined in the ontology to abbreviate
names of imported resources. For example, instead of writing
http://www.w3.org/2002/07/owl#Class,
we can write owl:Class because "owl" is
a prefix for the namespace "http://www.w3.org/2002/07/owl#".
Similarly, if an ontology imports the travel ontology from above, it
can define a prefix to access all imported classes and properties with
"travel", e.g. travel:Destination.
If we are inside the default namespace of the ontology, the prefix is
empty, i.e., the name of the resource is
only Destination. Application
developers can take control of namespace prefixes using the
NamespaceManager object.
In order to access the current NamespaceManager of an OWLModel, you can
use OWLModel.getNamespaceManager().
Assuming we have loaded the travel ontology as a default namespace, we
can access the resources contained in the OWLModel using the following
example calls:
OWLNamedClass destinationClass = owlModel.getOWLNamedClass("Destination");
OWLObjectProperty hasContactProperty = owlModel.getOWLObjectProperty("hasContact");
OWLDatatypeProperty hasZipCodeProperty = owlModel.getOWLDatatypeProperty("hasZipCode");
OWLIndividual sydney = owlModel.getOWLIndividual("Sydney");
... and use the objects to perform further queries or operations on the
corresponding resources. For example, in order to extract the URI for
a named object, you can use the
RDFResource.getURI() method.
RDFResource, from which subinterfaces for
classes,
properties,
and
individuals
are
derived:
RDFResource defines basic operations for all resources, in particular
getting and setting property values. RDFProperty is the base interface
for rdf:Properties and its subtypes owl:DatatypeProperty and owl:ObjectProperty.
For classes, the hierarchy gets quite complex because of the various
types of anonymous classes in OWL. This will be handled later.
Here is some example code that creates a simple class, an individual of that class, and then assigns a couple of properties.
OWLModel owlModel = ProtegeOWL.createJenaOWLModel();
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLDatatypeProperty ageProperty = owlModel.createOWLDatatypeProperty("age");
ageProperty.setRange(owlModel.getXSDint());
ageProperty.setDomain(personClass);
OWLObjectProperty childrenProperty = owlModel.createOWLObjectProperty("children");
childrenProperty.setRange(personClass);
childrenProperty.setDomain(personClass);
RDFIndividual darwin = personClass.createRDFIndividual("Darwin");
darwin.setPropertyValue(ageProperty, new Integer(0));
RDFIndividual holgi = personClass.createRDFIndividual("Holger");
holgi.setPropertyValue(childrenProperty, darwin);
holgi.setPropertyValue(ageProperty, new Integer(33));The Protégé-OWL API makes a clear distinction between named classes and anonymous classes. Named classes are used to create individuals, while anonymous classes are used to specify logical characteristics (restrictions) of named classes. We will handle anonymous classes later, but let's look at named classes now.
To reflect the OWL specification, there are two types of named classes:
RDFSNamedClass (rdfs:Class)
and OWLNamedClass (owl:Class).
Unless you are explicitly working in RDF, you will most likely create OWL
classes. After you have created the classes, you can arrange them in an
subclass relationship:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
// Create subclass (complicated version)
OWLNamedClass brotherClass = owlModel.createOWLNamedClass("Brother");
brotherClass.addSuperclass(personClass);
brotherClass.removeSuperclass(owlModel.getOWLThingClass());
In this example, the class Brother is created first as a top-level class.
The method
OWLModel.createOWLNamedClass(String name)
makes new classes by default a subclass of owl:Thing only. Then, Person is added to the
superclasses, leading to a situation in which both Person and owl:Thing are parents. Therefore,
owl:Thing needs to be removed afterwards.
A much more convenient way of creating a subclass of Person is as follows:
OWLNamedClass sisterClass = owlModel.createOWLNamedSubclass("Sister", personClass); The resulting inheritance hierarchy of the code above is:
Person
Brother
Sister
A simple recursive call can then be used to print arbitrary hierarchies with indentation:
printClassTree(personClass, "");
}
private static void printClassTree(RDFSClass cls, String indentation) {
System.out.println(indentation + cls.getName());
for (Iterator it = cls.getSubclasses(false).iterator(); it.hasNext();) {
RDFSClass subclass = (RDFSClass) it.next();
printClassTree(subclass, indentation + " ");
}
}
In the recursive routine, RDFSClass.getSubclasses() takes
a boolean argument. When set to true, this will return not only the direct
subclasses of the current
class, but also the subclasses of the subclasses, etc.
Named classes can be used to generate individuals. The instances of a class can then be queried using the
RDFSClass.getInstances()
method:
OWLIndividual individual = brotherClass.createOWLIndividual("Hans");
Collection brothers = brotherClass.getInstances(false);
assert (brothers.contains(hans));
assert (brothers.size() == 1);
There is a crucial distinction between "direct" and "indirect" instances. The individual
Hans is a direct instance of Brother, but not a direct instance of Person. However, it
is also an indirect instance of Person, because Brother is a subclass of Person, i.e.,
every Brother is also a Person. Programmers are able to select whether their calls shall address
only the direct or also the indirect instances using a boolean parameter:
assert (personClass.getInstanceCount(false) == 0);
assert (personClass.getInstanceCount(true) == 0);
assert (personClass.getInstances(true).contains(hans));
The inverse query to get the type/class of an individual can be made using the
RDFResource.getRDFType()
family of methods. For example, Hans has the rdf:type Brother, and it also has the (indirect)
type Person:
assert (hans.getRDFType().equals(brotherClass));
assert (hans.hasRDFType(brotherClass));
assert !(hans.hasRDFType(personClass, false));
assert (hans.hasRDFType(personClass, true));
If the life cycle of a resource is over, it can be deleted using the RDFResource.delete() method. The API uses
the same convention as other APIs,
where "delete" means to completely destroy the object, whereas "remove"
only deletes references to the object. This means that when you call delete
on a resource, then all its property values are removed but not deleted.
hans.delete();
To create an owl:DatatypeProperty, you can use
OWLModel.createOWLDatatypeProperty(String name).
By default, datatype properties can take any datatype value such as strings
and integers. OWL defines several XML Schema datatypes that can
be used to restrict the range of properties. The most popular XML Schema
datatypes are xsd:string, xsd:int, xsd:float, and xsd:boolean. For example,
if you want to limit your property to only take string values, you
can restrict its range using:
OWLDatatypeProperty property = owlModel.createOWLDatatypeProperty("name");
name.setRange(owlModel.getXSDstring());
... where the call OWLModel.getXSDstring()
returns a reference to the
RDFSDatatype
xsd:string.
Other default datatypes are accessible using similar OWLModel.getXSD...
methods. More complex datatypes can be accessed using their URI:
RDFSDatatype dateType = owlModel.getRDFSDatatypeByName("xsd:date");
For the default datatypes, property values are conveniently handled using
corresponding Java data types. For example, if you assign property values
for a string property, you can simply pass a String object to the setPropertyValue
call. Corresponding mappings exist into other data types:
individual.setPropertyValue(stringProperty, "MyString");
individual.setPropertyValue(intProperty, new Integer(42));
individual.setPropertyValue(floatProperty, new Float(4.2));
individual.setPropertyValue(booleanProperty, Boolean.TRUE);
The inverse getter methods will also deliver the objects in their simplest possible forms:
String stringValue = (String) individual.getPropertyValue(stringProperty);
Integer intValue = (Integer) individual.getPropertyValue(intProperty);
Float float = (Float) individual.setPropertyValue(floatProperty);
Boolean boolean = (Boolean) individual.setPropertyValue(booleanProperty);
Values of all other data types are wrapped into objects of the class
RDFSLiteral.
A literal combines a value together with its datatype. Values are stored
as strings and need to be unwrapped by the user code. The following example
is used to assign a value of the XML Schema datatype xsd:date.
RDFSDatatype xsdDate = owlModel.getRDFSDatatypeByName("xsd:date");
OWLDatatypeProperty dateProperty = owlModel.createOWLDatatypeProperty(
"dateProperty", xsdDate);
RDFSLiteral dateLiteral = owlModel.createRDFSLiteral("1971-07-06", xsdDate);
individual.setPropertyValue(dateProperty, dateLiteral);
RDFSLiteral myDate = (RDFSLiteral) individual.getPropertyValue(dateProperty);
System.out.println("Date: " + myDate);
... will print out "Date: 1971-07-06".
RDFSLiterals are also used to bundle string values together with a language tag:
RDFSLiteral langLiteral = owlModel.createRDFSLiteral("Wert", "de");
individual.setPropertyValue(stringProperty, langLiteral);
RDFSLiteral result = (RDFSLiteral) individual.getPropertyValue(stringProperty);
assert (result.getLanguage().equals("de"));
assert (result.getString().equals("Wert"));String,
Integer, Float, Boolean), or RDFSLiterals. If we have a literal of a
default data type, then this is automatically simplified. In some cases, it may be
more convenient to always have RDFSLiterals, in particular if user's
code has to execute on arbitrary data types. For these cases, the Protégé-OWL
API provides a number of convenience methods that are guaranteed to return RDFSLiterals,
e.g., OWLModel.asRDFSLiteral().owl:ObjectProperties are used to represent properties to establish relations
between individuals. The following snippet create a new object property
"children" that can take Persons as values: OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty childrenProperty = owlModel.createOWLObjectProperty("children");
childrenProperty.setRange(personClass); Then, the API can be used to assign property values for individuals:
RDFIndividual darwin = personClass.createRDFIndividual("Darwin");
RDFIndividual holgi = personClass.createRDFIndividual("Holger");
holgi.setPropertyValue(childrenProperty, darwin); The API also has various methods to add or remove values:
holgi.addPropertyValue(childrenProperty, other);
holgi.removePropertyValue(childrenProperty, other);
Let us now assume there is also a class Animal, and you want to specify that
the range of the children property is either Animals or Persons. In OWL
you need to create an owl:unionOf class to declare such ranges, because
declaring both classes as rdfs:ranges would mean that only objects that
are at the
same time Persons and Animals are valid for the property. Therefore, the
correct OWL declaration should look like the following:
<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Animal"/>
<owl:ObjectProperty rdf:ID="children">
<rdfs:range>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Person"/>
<owl:Class rdf:about="#Animal"/>
</owl:unionOf>
</owl:Class>
</rdfs:range>
</owl:ObjectProperty>
While it is rather complicated to deal with unions manually, the Protégé-OWL API makes it very simple to create union ranges on the fly:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLNamedClass animalClass = owlModel.createOWLNamedClass("Animal");
OWLObjectProperty childrenProperty = owlModel.createOWLObjectProperty("children");
childrenProperty.addUnionRangeClass(personClass);
childrenProperty.addUnionRangeClass(animalClass);
Object properties can also be declared to have other characterstics, e.g., they can be transitive. In the following code snippet, the property ancestor is declared to be transitive because if A is an ancestor of B, and B is an ancestor of C, then A is also an ancestor of C.
OWLObjectProperty ancestorProperty = owlModel.createOWLObjectProperty("ancestor");
ancestorProperty.setRange(personClass);
ancestorProperty.setTransitive(true);
Similar methods exist for making a property symmetric or functional.
Working with References to External/Untyped ResourcesIn many cases, resources in an ontology have references to other Web resources
that are not specified as OWL resources. For example, you may want to define
a link from an individual to an image. OWL/RDF documents can contain arbitrary links
into other URIs, such as the hasImage property value below.
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns="http://www.owl-ontologies.com/unnamed.owl#"
xml:base="http://www.owl-ontologies.com/unnamed.owl">
<owl:Ontology rdf:about=""/>
<owl:Class rdf:ID="Person"/>
<rdf:Property rdf:ID="hasImage"/>
<Person rdf:ID="Darwin">
<hasImage rdf:resource="http://www.knublauch.com/darwin/Darwin-Feeding-Smiling.jpg"/>
</Person>
</rdf:RDF>
The Protégé-OWL API supports such links by means of the
RDFUntypedResource
class. Untyped resources are individuals that have no rdf:type statement. Since
the concept of namespace prefixes does not consistently apply to untyped
resources, instances of the RDFUntypedResource class have the full URI
as their name. The following code snippet creates the example ontology shown above:
JenaOWLModel owlModel = ProtegeOWL.createJenaOWLModel();
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLIndividual individual = personClass.createOWLIndividual("Darwin");
RDFProperty hasImageProperty = owlModel.createRDFProperty("hasImage");
String uri = "http://www.knublauch.com/darwin/Darwin-Feeding-Smiling.jpg";
RDFUntypedResource image = owlModel.createRDFUntypedResource(uri);
individual.addPropertyValue(hasImageProperty, image);
Jena.dumpRDF(owlModel.getOntModel());The domain of a property specifies the types of resources that can take
values for the property. In the most trivial case, a property is domainless,
i.e., it does not have any rdfs:domain statement in the OWL file. This is
logically equivalent to having a domain consisting of only the class owl:Thing,
because every class is derived from owl:Thing. The default domain of a
new property in Protégé is null, meaning that it is domainless.
The API
provides several methods in the class RDFProperty to set and query the
domain. The following code snippet creates a new property and puts the class
Person into its domain:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty childrenProperty = owlModel.createOWLObjectProperty("children");
childrenProperty.setDomain(personClass);
In principle, properties can have multiple domain definitions. Similar
to range statements, having multiple domains means that the property can
be applied to the intersection of the domain classes. This seldom reflects
the modeler's intention, and the much more common case is to declare the
domain to be a union of various classes, implemented by means of an anonymous
owl:unionOf class. In the Protégé-OWL API, these union classes can be created
automatically using the following call:
OWLNamedClass animalClass = owlModel.createOWLNamedClass("Animal");
childrenProperty.addUnionDomainClass(animalClass); Resulting in the following OWL code:
<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Animal"/>
<owl:ObjectProperty rdf:ID="children">
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Person"/>
<owl:Class rdf:about="#Animal"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
</owl:ObjectProperty>
The handling of domains is a bit more complicated if you have subproperty hierarchies. If the subproperty
does not have it's own domain, then it inherits the domain of its superproperties. For example, if you have a property
sons, which is a subproperty of children, and you leave the domain of sons unspecified,
then it consists of Person and Animal as well:
OWLObjectProperty sonsProperty = owlModel.createOWLObjectProperty("sons");
sonsProperty.addSuperproperty(childrenProperty);
assert (sonsProperty.getDomain(false) == null);
assert (sonsProperty.getDomain(true) instanceof OWLUnionClass);
Here, the
RDFProperty.getDomain()
method takes a boolean flag to distinguish between the direct domain of the property and its
possibly inherited domain.
In the example above, the domain consists of an OWLUnionClass (which will be handled in detail below), to indicate
that either Person or Animal are valid domains. Since this is a common pattern, the following
convenience method
RDFProperty.getUnionDomain()
resolves union domains into a handy collection of classes:
Collection unionDomain = sonsProperty.getUnionDomain(true);
assert (unionDomain.contains(personClass));
assert (unionDomain.contains(animalClass));The Protégé-OWL API distinguishes between named classes (RDFSNamedClass and
its subinterface OWLNamedClass)
and anonymous classes (OWLAnonymousClass and
its subinterfaces).
Anonymous classes can be used to logically describe named classes. This
means that the life cycle of any anonymous class completely depends on
the named
class, i.e., if the named class is deleted, then the depending anonymous
classes will also be deleted. Anonymous classes can in some places also
be used to
describe properties, in particular in domain and range statements, and
in these cases as well they depend on the host properties.
Here is an overview of the anonymous class interfaces in the Protégé-OWL API:
RDFSClass
OWLClass
OWLAnonymousClass
OWLRestriction
OWLQuantifierRestriction
OWLAllValuesFrom
OWLSomeValuesFrom
OWLHasValue
OWLCardinalityBase
OWLCardinality
OWLMinCardinality
OWLMaxCardinality
OWLEnumeratedClass
OWLLogicalClass
OWLComplementClass
OWLNAryLogicalClass
OWLIntersectionClass
OWLUnionClass
The subinterfaces of OWLRestriction are used to define restrictions on
property values. For example, an OWLMinCardinality restriction on a property
at a class indicates that for instances of the class, a minimum
number of values is required. The following code segment creates such a
restriction
on the property hasChildren at
the class Parent:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty hasChildrenProperty = owlModel.createOWLObjectProperty("hasChildren");
hasChildrenProperty.setDomain(personClass);
OWLNamedClass parentClass = owlModel.createOWLNamedSubclass("Parent", personClass);
OWLMinCardinality minCardinality = owlModel.createOWLMinCardinality(hasChildrenProperty, 1);
parentClass.addSuperclass(minCardinality);
The above code results in the following OWL model:
<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Parent">
<rdfs:subClassOf> <owl:Restriction> <owl:onProperty>
<owl:ObjectProperty rdf:ID="hasChildren"/>
</owl:onProperty>
<owl:minCardinality
rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1
</owl:minCardinality>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:ObjectProperty rdf:about="#hasChildren">
<rdfs:domain rdf:resource="#Person"/>
</owl:ObjectProperty>.
The class Parent now has two superclasses; the named class Person and the anonymous restriction.
Subsequent calls to RDFSClass.getSuperclasses()
will return both of them, while RDFSClass.getNamedSuperclasses()
will only return Person.
Now consider the following, more complicated scenario. The named class Superclass has a subclass
Subclass and both have an owl:allValuesFrom restriction on a property:
<owl:Class rdf:ID="Superclass">
<rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
<rdfs:subClassOf>
<owl:Restriction>
<owl:allValuesFrom>
<owl:Class rdf:ID="Subclass"/>
</owl:allValuesFrom>
<owl:onProperty>
<owl:ObjectProperty rdf:ID="property"/>
</owl:onProperty>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
<owl:Class rdf:about="#Subclass">
<rdfs:subClassOf>
<owl:Restriction>
<owl:allValuesFrom rdf:resource="#Subclass"/>
<owl:onProperty>
<owl:ObjectProperty rdf:about="#property"/>
</owl:onProperty>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf rdf:resource="#Superclass"/>
</owl:Class>
The code to produce this scenario looks as follows:
OWLNamedClass superclass = owlModel.createOWLNamedClass("Superclass");
OWLNamedClass subclass = owlModel.createOWLNamedSubclass("Subclass", superclass);
OWLObjectProperty property = owlModel.createOWLObjectProperty("property");
OWLAllValuesFrom superRestriction = owlModel.createOWLAllValuesFrom(property, subclass);
superclass.addSuperclass(superRestriction);
OWLAllValuesFrom subRestriction = owlModel.createOWLAllValuesFrom(property, subclass);
subclass.addSuperclass(subRestriction);
Now, if we want to ask the subclass about its restrictions, we are probably not interested in the
owl:allValuesFrom restriction defined
for the superclass, because the restriction at the subclass "overloads" the
superclass restriction. Therefore, the following code only returns the subclass
restriction:
Collection restrictions = subclass.getRestrictions(true);
assert (1 == restrictions.size());
assert (restrictions.contains(subRestriction);
If we want to know which types of values for the property are allowed
at the subclass, we can ask for the corresponding owl:allValuesFrom restrictions.
The following code iterates on all restrictions
that are defined on the given class and checks if any of them are owl:allValuesFrom
restrictions. If success, the filler of that restriction is the result.
If no matching restriction has been found, then the global range of the
property is returned:
public RDFResource getAllValuesFrom(OWLNamedClass cls, RDFProperty property) {
Collection restrictions = cls.getRestrictions(property, true);
for (Iterator it = restrictions.iterator(); it.hasNext();) {
OWLRestriction restriction = (OWLRestriction) it.next();
if(restriction instanceof OWLAllValuesFrom) {
return ((OWLAllValuesFrom)restriction).getAllValuesFrom();
}
}
return property.getRange();
}
In fact there is a pre-defined convenience method
OWLNamedClass.getAllValuesFrom()
for exactly this case:
assert (subclass.equals(subclass.getAllValuesFrom(property)));
Similar mechanisms exist for cardinality restrictions, which we will look at in the following scenario. A superclass defines a maximum cardinality restriction of 3 for the property. This restriction is overloaded in the subclass to 2 (note that according to the semantics of OWL, subclasses can only narrow restrictions from their superclasses):
<owl:Class rdf:ID="Superclass">
<rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
<rdfs:subClassOf>
<owl:Restriction>
<owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">3</owl:maxCardinality>
<owl:onProperty>
<owl:ObjectProperty rdf:ID="property"/>
</owl:onProperty>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
<owl:Class rdf:ID="Subclass">
<rdfs:subClassOf>
<owl:Restriction>
<owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">2</owl:maxCardinality>
<owl:onProperty rdf:resource="#property"/>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf rdf:resource="#Superclass"/>
</owl:Class>
The following code creates this scenario:
OWLNamedClass superclass = owlModel.createOWLNamedClass("Superclass");
OWLNamedClass subclass = owlModel.createOWLNamedSubclass("Subclass", superclass);
OWLObjectProperty property = owlModel.createOWLObjectProperty("property");
OWLMaxCardinality superRestriction = owlModel.createOWLMaxCardinality(property, 3);
superclass.addSuperclass(superRestriction);
OWLMaxCardinality subRestriction = owlModel.createOWLMaxCardinality(property, 2);
subclass.addSuperclass(subRestriction);
In this scenario, the following condition is true:
assert (2 == subclass.getMaxCardinality(property));
Note that in addition to owl:maxCardinality restrictions, there is another
way in OWL to enforce maximum cardinalities. If a property is declared
to be an owl:FunctionalProperty, then it can only take one global (distinct)
value. In our example scenario, if we make the property functional, then the following
code is also correct, no matter which other restrictions are defined at
the class level:
property.setFunctional(true);
assert (1 == subclass.getMaxCardinality(property));Logical class definitions can be used to build complex class expressions
out of restrictions and named classes. Like restrictions, logical classes
are only meaningful if they are attached to a specific named class or property. This time, we will start with a complex
example right away in which we have a class Person with subclasses Man and
Woman. In addition, there is a class Kid, which is a subclass of an anonymous expression
that describes all individuals that are Persons and at the same time neither Men nor
Women:
<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Woman">
<rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:Class rdf:ID="Man">
<rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>
<owl:Class rdf:ID="Kid">
<rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
<rdfs:subClassOf>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Person"/>
<owl:Class>
<owl:complementOf>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Man"/>
<owl:Class rdf:about="#Woman"/>
</owl:unionOf>
</owl:Class>
</owl:complementOf>
</owl:Class>
</owl:intersectionOf>
</owl:Class>
</rdfs:subClassOf>
</owl:Class>
The Java code to create these definitions is as follows:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLNamedClass manClass = owlModel.createOWLNamedSubclass("Man", personClass);
OWLNamedClass womanClass = owlModel.createOWLNamedSubclass("Woman", personClass);
// Create expression (PersonClass & !(Man | Woman))
OWLUnionClass unionClass = owlModel.createOWLUnionClass();
unionClass.addOperand(manClass);
unionClass.addOperand(womanClass);
OWLComplementClass complementClass = owlModel.createOWLComplementClass(unionClass);
OWLIntersectionClass intersectionClass = owlModel.createOWLIntersectionClass();
intersectionClass.addOperand(personClass);
intersectionClass.addOperand(complementClass);
OWLNamedClass kidClass = owlModel.createOWLNamedClass("Kid");
kidClass.addSuperclass(intersectionClass);
Analyzing these complex structures can be achieved by means of the corresponding
interface methods from the Protégé-OWL API. For example, there is a
getOperands()
method defined for OWLUnionClass and OWLIntersectionClass.
While the construction of such expressions is often complicated, the process can be made easier by using
the OWLModel.createRDFSClassFromExpression()
method. The intersection shown above can also be created in the following way:
owlModel.createRDFSClassFromExpression("Person & !(Man | Woman)");
The Protégé-OWL API can be used to build arbitrarily complex expressions from existing expressions. The following code first converts the existing intersection class into an expression, and then adds a complement symbol (!) to create a new class.
String parsable = intersectionClass.getParsableExpression();
System.out.println("Expression: " + parsable);
RDFSClass c = owlModel.createRDFSClassFromExpression("!(" + parsable + ")");
System.out.println("New expression: " + c.getParsableExpression());
The program output is:
Expression: Person & !(Man | Woman)
New expression: !(Person & !(Man | Woman))
If you want to print expressions using the logical symbols, you can use
the getBrowserText()
method.
An enumerated class is an anonymous class that lists all of its individuals explicitly. A typical example is a
class TrafficLightColor consisting of the individuals red, yellow, and
green:
<owl:Class rdf:ID="Color"/>
<Color rdf:ID="black"/>
<owl:Class rdf:ID="TrafficLightColor">
<owl:equivalentClass>
<owl:Class>
<owl:oneOf rdf:parseType="Collection">
<Color rdf:ID="red"/>
<Color rdf:ID="yellow"/>
<Color rdf:ID="green"/>
</owl:oneOf>
</owl:Class>
</owl:equivalentClass>
</owl:Class>
The code to generate the OWL model shown above is as follows:
OWLNamedClass colorClass = owlModel.createOWLNamedClass("Color");
OWLIndividual red = colorClass.createOWLIndividual("red");
OWLIndividual yellow = colorClass.createOWLIndividual("yellow");
OWLIndividual green = colorClass.createOWLIndividual("green");
OWLIndividual black = colorClass.createOWLIndividual("black");
OWLNamedClass trafficLightColor = owlModel.createOWLNamedClass("TrafficLightColor");
OWLEnumeratedClass enum = owlModel.createOWLEnumeratedClass();
enum.addOneOf(red);
enum.addOneOf(yellow);
enum.addOneOf(green);
trafficLightColor.setDefinition(enum);
Note that this example does not use rdfs:subClassOf to associate the named
class with the anonymous class. Instead, the example uses owl:equivalentClass,
which is described next.
In Description Logics languages like OWL, classes can be either primitive or defined. Primitive classes only have necessary conditions, i.e., superclasses. Defined classes have necessary and sufficient conditions, i.e., equivalent classes. For a more comprehensive discussion of these aspects, please read the Protégé-OWL Tutorial by Matthew Horridge.
A typical example for a defined class is the Parent concept, defined as the set of all persons that have at
least one child:
<owl:Class rdf:ID="Person"/>
<owl:Class rdf:ID="Parent">
<owl:equivalentClass>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Person"/>
<owl:Restriction>
<owl:onProperty>
<owl:ObjectProperty rdf:ID="hasChildren"/>
</owl:onProperty>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>1</owl:minCardinality>
</owl:Restriction>
</owl:intersectionOf>
</owl:Class>
</owl:equivalentClass>
</owl:Class>
<owl:ObjectProperty rdf:about="#hasChildren">
<rdfs:domain rdf:resource="#Person"/>
</owl:ObjectProperty>
Technically, the class Parent is equivalent to the anonymous intersection of Person and the
minimum cardinality restriction. The following code snippet creates the example from above:
OWLNamedClass personClass = owlModel.createOWLNamedClass("Person");
OWLObjectProperty hasChildrenProperty = owlModel.createOWLObjectProperty("hasChildren");
hasChildrenProperty.setDomain(personClass);
OWLNamedClass parentClass = owlModel.createOWLNamedClass("Parent");
OWLMinCardinality minCardinality = owlModel.createOWLMinCardinality(hasChildrenProperty, 1);
OWLIntersectionClass intersectionClass = owlModel.createOWLIntersectionClass();
intersectionClass.addOperand(personClass);
intersectionClass.addOperand(minCardinality);
parentClass.setDefinition(intersectionClass);OWLNamedClass.setDefinition(),
which declares the named class to be equivalent to the anonymous class. Following this call, the class Person is
also a explicitly made a superclass of Parent. As a general rule, whenever an intersection class is made equivalent
to a named class N, then all named operands of the intersection are also made superclasses of N. This makes it much easier to
handle these defined classes because it makes the obvious subclass relationship explicit without the need to execute a reasoner
first. Without this behavior, it would be much more difficult to display and walk through the class hierarchy.While most of the examples so far were used to create resources in an
OWLModel, we will now look at how to query and traverse the contents of
an ontology. The following code iterates through all user-defined named classes
in the "travel.owl" ontology and prints their instances:
String uri = "http://protege.cim3.net/file/pub/ontologies/travel/travel.owl";
//alternatively, you can specify a local path on your computer
//for the travel.owl ontology. Example:
//String uri = "file:///c:/Work/Projects/travel.owl"
OWLModel owlModel = ProtegeOWL.createJenaOWLModelFromURI(uri);
Collection classes = owlModel.getUserDefinedOWLNamedClasses();
for (Iterator it = classes.iterator(); it.hasNext();) {
OWLNamedClass cls = (OWLNamedClass) it.next();
Collection instances = cls.getInstances(false);
System.out.println("Class " + cls.getBrowserText() + " (" + instances.size() + ")");
for (Iterator jt = instances.iterator(); jt.hasNext();) {
OWLIndividual individual = (OWLIndividual) jt.next();
System.out.println(" - " + individual.getBrowserText());
}
}
This program produces the following output (using the "travel.owl" ontology):
Class Town (1)
- Coonabarabran
Class UrbanArea (0)
Class Destination (0)
Class RuralArea (2)
- CapeYork
- Woomera
Class QuietDestination (0)
Class FamilyDestination (0)
...
Note that the OWLModel contains many more classes than those returned by
the getUserDefined... methods. These methods filter out the system resources
such as the owl:Class metaclass, owl:Thing and rdfs:subClassOf. If you
want to access other resources by their names, you can easily use methods such as
OWLModel.getOWLNamedClass()
or
OWLModel.getOWLObjectProperty().
In summary, the OWLModel class provides access to all the resources in the model. After you have gained
access to the resource objects via the OWLModel, you can then call other getter methods.
To a certain extent, other types of queries are also supported. For example, you can use
OWLModel.getRDFResourcesWithPropertyValue()
to get all resources that have a certain property value. These properties
can also be system properties, and the next example code snippet performs
a query of all resources that have owl:Thing as their superclass:
RDFProperty subClassOfProperty = owlModel.getRDFProperty(RDFSNames.Slot.SUB_CLASS_OF);
OWLNamedClass owlThingClass = owlModel.getOWLThingClass();
Collection results = owlModel.getRDFResourcesWithPropertyValue(subClassOfProperty, owlThingClass);
System.out.println("Subclasses of owl:Thing:");
for (Iterator it = results.iterator(); it.hasNext();) {
RDFResource resource = (RDFResource) it.next();
System.out.println(" - " + resource.getBrowserText());
}
For more advanced queries in languages like RDQL, you need to use external
libraries as there is currently no native support in Protégé-OWL for them.
For example, you could access a Jena OntModel on the fly and then perform
queries on this OntModel. Instructions for accessing the Jena model are provided later.
The Protégé-OWL API is not limited to OWL. Since OWL is an extension
of RDF Schema, the API also has native support for handling all relevant
RDF elements. In particular, it is possible to create and query pure RDF
files, using classes such as RDFSNamedClass, RDFProperty, and RDFIndividual
instead of their OWL-specific extensions. It is also easy to create hybrid
solutions that make use of selected OWL features to overcome limitations
of pure RDF. For example, you can assign union classes as ranges or domains
to an RDFProperty.
The following code snippet shows a typical sequence for creating an RDF ontology:
JenaOWLModel owlModel = ProtegeOWL.createJenaOWLModel();
RDFSNamedClass personClass = owlModel.createRDFSNamedClass("Person");
RDFProperty ageProperty = owlModel.createRDFProperty("age");
ageProperty.setRange(owlModel.getXSDint());
ageProperty.setDomain(personClass);
RDFIndividual individual = personClass.createRDFIndividual("Holger");
individual.setPropertyValue(ageProperty, new Integer(33));
Jena.dumpRDF(owlModel.getOntModel());
The resulting output is the following OWL/RDF file:
<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns="http://www.owl-ontologies.com/unnamed.owl#"
xml:base="http://www.owl-ontologies.com/unnamed.owl">
<owl:Ontology rdf:about=""/>
<rdfs:Class rdf:ID="Person"/>
<rdf:Property rdf:ID="age">
<rdfs:domain rdf:resource="#Person"/>
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#int"/>
</rdf:Property>
<Person rdf:ID="Holger">
<age rdf:datatype="http://www.w3.org/2001/XMLSchema#int">33</age>
</Person>
</rdf:RDF>
If the owl:Ontology tag is not desired in the output, it can be easily stripped from the Jena model before
the file is saved.
The Protégé-OWL API supports the typical Model-view-controller architecture. This means that the model stores the representation of the ontology data, and changes in that model trigger events which external components such as UI widgets can react to. The OWL API follows the typical observer design pattern known from Swing and other UI libraries to implement such event mechanisms. Here is a trivial example, which reacts on instance creation for a given class:
OWLNamedClass cls = owlModel.createOWLNamedClass("Class");
cls.addClassListener(new ClassAdapter() {
public void instanceAdded(RDFSClass cls, RDFResource instance) {
System.out.println("Instance was added to class: " + instance.getName());
}
});
for(int i = 0; i < 5; i++) {
String newName = "Individual" + (int)(Math.random() * 10000);
cls.createOWLIndividual(newName);
}
The basic idea is that a dedicated object called a listener registers with
the model and from then on will receive notifications about changes in
that object. In the example above, the listener is an instance of the
ClassListener
interface, which is registered at the given named class. Other listener types are
PropertyListener
(for property-related events),
PropertyValueListener
(for arbitrary property value changes at any resource), and
ResourceListener
for type changes of a resource. There is also a very powerful listener type
ModelListener,
which can be used to detect changes the affect the OWLModel in general, e.g., if a new class is created.
This is demonstrated in the following code snippet:
owlModel.addModelListener(new ModelAdapter() {
public void propertyCreated(RDFProperty property) {
System.out.println("Property created: " + property.getName());
}
});
owlModel.createRDFProperty("RDF-Property");
owlModel.createOWLObjectProperty("Object-Property");
owlModel.createOWLDatatypeProperty("Datatype-Property");
The code shown above will print out three lines after each create statement. The main trick here is that event handling can be completely decoupled. This is typically used in interactive user interfaces, where an action is required after a user has done a certain action, but it is not known in advance when the user will perform the action.
Loading and Saving FilesThe Protégé-OWL API can be used in two storage modes:
JenaOWLModel)OWLDatabaseModel)Both modes operate on the same OWLModel core interface. Method calls to do things like create classes are
totally independent from the storage mode. Programmers do not have to care whether the class is physically created in a
database or only kept in memory. There are some subtle differences between the two modes as both have some
extra functions.
OWL Files mode is based on the JenaOWLModel class. You can use the static
methods from the ProtegeOWL class to load an existing OWL file from a stream or a URL. The calls will return a
JenaOWLModel, and you can then use the save methods in the JenaOWLModel to write the file back
to disk:
String uri = "http://protege.cim3.net/file/pub/ontologies/travel/travel.owl";
//alternatively, you can specify a local path on your computer
//for the travel.owl ontology. Example:
//String uri = "file:///c:/Work/Projects/travel.owl"
JenaOWLModel owlModel = ProtegeOWL.createJenaOWLModelFromURI(uri);
OWLNamedClass destinationClass = owlModel.getOWLNamedClass("Destination");
// ...
String fileName = "travel-saved.owl";
Collection errors = new ArrayList();
owlModel.save(new File(fileName).toURI(), FileUtils.langXMLAbbrev, errors);
System.out.println("File saved with " + errors.size() + " errors.");OWL ontologies can import other ontologies and thus establish relationships between resources from multiple files. For example, it is a common design pattern to keep instance data separate from classes and properties. In this case, the instances ontology would import the ontology that defines the classes. The Protégé user interface allows users to switch the "active" sub-ontology, so that users can select which file changes will be propogated to.
Protégé stores OWL ontologies in triple tables, one table for each file. These tables are now named after
the Java interface
TripleStore.
Each OWLModel manages a list of TripleStores by means of its
TripleStoreModel.
An empty project has two TripleStores: The system TripleStore containing the system classes such as
owl:Class, and the main TripleStore. If your project imports other ontologies, you can change the
active TripleStore using the corresponding TripleStoreModel methods.
When you save your project, the saver will overwrite all files that have an alternative file:-entry in the ont-policy file, and which are declared to be editable.
Working with Jena ModelsProtégé-OWL uses the popular Jena API for various
tasks, in particular for parsing of OWL/RDF files. Furthermore, the Protégé-OWL API can be used to generate a
Jena OntModel at any time. Both JenaOWLModel and OWLDatabaseModel implement the
OntModelProvider
interface, which has a getOntModel()
method to create an OntModel from the current ontology.
JenaOWLModel owlModel = ProtegeOWL.createJenaOWLModel();
OWLNamedClass cls = owlModel.createOWLNamedClass("Class");
OWLDatatypeProperty property = owlModel.createOWLDatatypeProperty("property");
OWLIndividual individual = cls.createOWLIndividual("Individual");
OntModel ontModel = owlModel.getOntModel();
OntClass ontClass = ontModel.getOntClass(cls.getURI());
DatatypeProperty ontProperty = ontModel.getDatatypeProperty(property.getURI());
Individual ontIndividual = ontModel.getIndividual(individual.getURI());OntModel. In particular you can use any Jena-based services
such as querying or saving files. Note however, that you currently need
to call getOntModel() after each change if you want to have an OntModel that
is synchronized with the current OWLModel. This may be a time consuming
procedure for large ontologies.The Protégé-OWL API is an extension of the core Protégé API. For those users who are not familiar with Core Protégé, there is a different mode of the system (collectively referred to as Core Protégé and Protégé-Frames) based on a metamodel called OKBC. This "core" mode predates the OWL mode of Protégé by many years. OKBC is simpler than OWL and uses different concepts but also has metaclasses. These metaclasses were used for the representation of OWL in terms of OKBC in Protégé-OWL. This means that Protégé-OWL is technically an extension of the core Protégé system. More information about the differences between Protégé-Frames and Protégé-OWL is available in the overview section of our Web site.
The Core Protégé system comes with a comprehensive API for plug-in
developers. This API is optimized for OKBC and operates on concepts
such as frames and slots. The Protégé-OWL API extends the core API. For example,
the OWL interface RDFProperty is derived from the core interface
Slot.
However, OWL users don't need to learn the details of the OKBC metamodel
and are not encouraged to use these low-level features in their programs.
Operating on the low-level core API for OWL applications requires a solid
understanding of what happens under the hood and is only recommended for
people who need to develop plug-ins that operate in both core and OWL modes.
For
the convenience of OWL developers, the Protégé-OWL API hides all details
of the core system. A deprecation mechanism is used to "deactivate"
those methods that are inherited from the core system. If you are using
a Java IDE like IntelliJ or Eclipse, the use of deprecated methods will
be reported at edit and compile time. For example, instead of using
the core method
Slot.addDirectSuperslot(),
you will use RDFProperty.addSuperproperty().
The deprecation mechanism does not mean though that the core methods
will go away. They still can be used but they are not safe unless you really
know what you are doing.
If you are familiar with the core Protégé API and need to use a feature
in the OWL API, first look at the Javadoc of the wrapper interfaces such
as
ProtegeSlot
and
ProtegeCls.
These will have a @seealso tag at deprecated
methods pointing to the corresponding OWL method. If a method does not
have an OWL equivalent, then it most likely does not make sense in OWL.
If you are missing some functionality, please let us know so that we can
determine whether a method is missing.
If you have never used the old Protégé-OWL API, you can skip this section.
In the new version of the Protégé-OWL API, virtually all of the classes from the old model were renamed and
completely changed. No effort was made to make the transition an evolutionary one, because the changes were just too profound.
The purpose behind creating the new API was to create an API that closely resembled the OWL language definition but
also kept things simple wherever possible.
Many of the changes are purely syntactical; all occurrences of the term slot were replaced with "property", etc.
The top-level OWLModel object was formerly known as OWLKnowledgeBase. All of these classes inherit
from the core Protégé classes but the Protégé details are hidden using a set of abstract base
interfaces ProtegeCls, ProtegeSlot, ProtegeInstance and ProtegeKnowledgeBase,
which declare almost all Protégé methods deprecated.
If you are converting an old Protégé-based Java program to the new Protégé-OWL API, search for
all occurrences of "Cls", "Slot",
"Instance" and "KnowledgeBase",
and replace them with RDFSClass, RDFProperty, RDFResource and OWLModel. After
that you should get many errors or warnings to find usages of deprecated methods.
The following guidelines may help you during the transition.
unionOf. This is reflected with the term
UnionDomain in several places in the API.ValueTypes
(Boolean, Float, Integer, String, Symbol, Instance,
and Cls), whereas OWL has a much bigger set of XML Schema
datatypes.
Also, literal values are tagged with type information. The notion of ValueType
has been completely replaced in the Protégé-OWL API. Now, programmers can
set the range of a property and assign either an RDFDatatype (primitive values),
an OWLDataRange (an extension of symbol enumerations), or a class to the range.
If in the latter case multiple classes are needed, most users intend to have
union
semantics,
so that a union class needs to be used as range. The API provides convenient
methods to create such unions on the fly. For RDFDatatypes, the model contains
the collection of predefined elements like xsd:int. The handling of literal
property values is described below. For legacy components, the OWL API tries
to map a property's range into the old ValueTypes and allowed classes. There
is no mapping into the value types Symbol and Cls anymore, and allowed
parents are stored as annotation properties (protege:allowedParent) only.getSuperclasses and getDirectSuperclasses. Experience
has shown that in most cases, programmers need the "direct" calls.
Furthermore, some users found that the "direct" calls were inconsistent
with the add/remove methods which did not always have the "direct" option.
The Protégé-OWL API changes this approach and programmers must pay attention
not to confuse the
new calls with the old
ones.
For example, there is now a single getSuperclasses method which takes a boolean
parameter to indicate whether the direct (false) or transitive (true) values
shall be returned. Methods where this is not specified assume direct values
only, as in isSubclassOf or getSuperclassCount.For the convenience of developers who were used to the old Protégé-OWL API, the following table lists the new names of frequently used methods:
| Old Protégé-OWL API | New Protégé-OWL API |
addDirectTemplateSlot |
addUnionDomainClass |
addDirectSuperclass |
addSuperclass |
createDirectInstance |
createInstance |
getDirectOwnSlotValue |
getPropertyValue |
getOwnSlotValue |
getPropertyValue(true) |
getDirectSuperSlots |
getSuperproperties(false) |
getSuperSlots |
getSuperproperties(true) |
hasDirectSuperclass |
isSubclassOf |
getDirectDomain |
getUnionDomain |
getDomain |
getUnionDomain(true) |
The Protégé API not only has a non-visual model part, but also comes with comprehensive support for user interface programming. There are several convenient classes and utility methods that help programmers develop interactive user interfaces quickly and with uniform look-and-feels that match the rest of the Protégé family of tools.
One of the foundations of UI programming with Protégé is the event mechanism,
which allows programmers to react cleanly on changes. This and
some other aspects will be illustrated in the following example application.
Let us assume we want to have a list of all instances of a certain class
(the Destination class from the travel ontology) and want to easily allow
users to add new instances. Such a component could consist of a
JList displaying the entries, a button to add new entries, and control logic
that updates the list in response to add actions.
When the application is started, the following list is displayed in a JFrame:

Destination is shown:
After the user has selected a class, a small dialog asks for the name of the new Destination instance:
... and the new instance is added:
Here is the source code for the main method of this small application:
public static void main(String[] args) throws Exception {
String uri = "http://protege.cim3.net/file/pub/ontologies/travel/travel.owl";
//alternatively, you can specify a local path on your computer
//for the travel.owl ontology. Example:
//String uri = "file:///c:/Work/Projects/travel.owl"
OWLModel owlModel = ProtegeOWL.createJenaOWLModelFromURI(uri);
OWLNamedClass destinationClass = owlModel.getOWLNamedClass("Destination");
ListPanel listPanel = new ListPanel(destinationClass);
JFrame frame = new JFrame("Simple List Example");
Container cont = frame.getContentPane();
cont.setLayout(new BorderLayout());
cont.add(BorderLayout.CENTER, listPanel);
frame.setBounds(100, 100, 300, 300);
frame.setVisible(true);
}
Essentially, the main method loads the travel ontology and then creates a ListPanel, which is finally put into a
JFrame to appear for the user. The source code for the ListPanel class is shown next:
private static class ListPanel extends JPanel implements Disposable {
private OWLNamedClass destinationClass;
private JList list;
private DefaultListModel listModel;
private ModelListener modelListener = new ModelAdapter() {
public void individualCreated(RDFResource resource) {
if (resource.hasRDFType(destinationClass, true)) {
handleDestinationAdded(resource);
}
}
};
private OWLModel owlModel;
ListPanel(OWLNamedClass activityClass) {
this.destinationClass = activityClass;
this.owlModel = activityClass.getOWLModel();
owlModel.addModelListener(modelListener);
listModel = new DefaultListModel();
for (Iterator it = activityClass.getInstances(true).iterator(); it.hasNext();) {
OWLIndividual individual = (OWLIndividual) it.next();
listModel.addElement(individual);
}
list = new JList(listModel);
// Make sure list entries show up nicely with icons
list.setCellRenderer(new ResourceRenderer());
// Wrap the list together with a button bar
OWLLabeledComponent lc = new OWLLabeledComponent("Destinations", new JScrollPane(list));
lc.addHeaderButton(new AbstractAction("Add Destination...",
OWLIcons.getAddIcon(OWLIcons.RDF_INDIVIDUAL)) {
public void actionPerformed(ActionEvent e) {
addDestination();
}
});
// Add everything into the JPanel
setLayout(new BorderLayout());
add(BorderLayout.CENTER, lc);
}
private void addDestination() {
OWLNamedClass newType = OWLUI.pickOWLNamedClass(owlModel,
Collections.singleton(destinationClass), "Select type of new Destination");
if (newType != null) {
String name = JOptionPane.showInputDialog(
"Enter name of new " + newType.getBrowserText());
if (name != null) {
newType.createOWLIndividual(name);
}
}
}
public void dispose() {
owlModel.removeModelListener(modelListener);
}
private void handleDestinationAdded(RDFResource destination) {
listModel.addElement(destination);
list.setSelectedValue(destination, true);
}
}
This class maintains a reference to the Destination class, which it needs to create instances later. It also
has fields to manage the JList and its model. The ListModel will have one row for each destination
instance. The ListModel is decoupled from the user interface. It is updated only if the OWLModel
reports that a new individual of the Destination class has been added. The relevant object to implement this is
the ModelListener, which is registered on the OWLModel in the constructor. This also makes sure that
the list will be updated even if instances are created somewhere else in the user interface. Note that after termination, the
dialog should also remove the listener from the OWLModel (indicated here by means of the dispose method).
The rest of the code uses a convenience method from the OWLUI class to
select a named class from a tree. This is part of the Protégé library and
can easily be reused, just like the OWLLabeledComponent class with the
header button support. Many of the existing Protégé components can be subclassed
and thus serve as the foundation for completely new applications and Protégé
plug-ins.
Protégé provides several extension points where developers can dynamically add components as so-called plug-ins. The following (slightly outdated) image illustrates the types of plug-ins that you can create for the Protégé-OWL editor. The Protégé Programming Development Kit (PDK) has a lot of general information on how to write, package, and distribute plug-ins. The best way to get started is to examine existing plug-ins that were written for the Protégé-OWL editor, e.g., OWLViz, OWLDoc, Protégé Wizards, etc. Pay particular attention to the manifest and protege.properties files for these plug-ins. The latter is needed for your plug-in to declare dependency on the Protégé-OWL editor.

| T | Tab widget plug-ins are a Core Protégé feature. A tab widget is a plug-in that appears as one of the main tabs on the screen. Examples of tab plug-ins are OWLClasses, Properties and Metadata. You can add arbitrary services to your tab, and you can "listen" to any events in the ontology to adjust the display. In order to activate a tab widget in the user interface, a user needs to select the Project | Configure... menu item. (more) |
| S | Slot widget plug-ins are a Core Protégé feature. A slot widget is a plug-in that can display and edit a property value on a form. Examples of default slot widgets include the list of disjoint classes, the conditions widget, and the annotation properties widget. You can create your own slot widgets and add them to the forms using the Forms tab. (more) |
| P | Project plug-ins are a Core Protégé feature. They allow programmers to execute arbitrary code when a project is created, loaded, or closed. In particular, they can be used to add menus or toolbar buttons. They can also be used to attach arbitrary listeners to a knowledge base, such as agents. (more) |
| F | Resource action plug-ins can appear in the right-click
menu of a selected class, property, or individual, or in the lower
left
corner of a form (as shown by the second ‘F’ above). A resource action plug-in
must be a subclass of ResourceAction and you need to add an entry "ResourceAction=True"
to your manifest file. Then, the ResourceAction is able to decide
whether it wants to appear in the context menu, or also in the
icon bar
at the bottom left corner of a form. |
| I | Resource display plug-ins can be used to add
arbitrary components to the lower right corner of each form. You
need to
subclass
ResourceDisplayPlugin and add an entry "ResourceDisplayPlugin=True"
to your manifest file. Then you get a reference to a resource
(e.g. an owl:Class) and a JPanel in which you can add buttons or
other
small components. |
| O | Ontology test plug-ins are plug-ins that will be executed
when the user presses the test ontology buttons. Each test must
be a subclass of OWLTest and requires a manifest entry (check the
Protégé-OWL editor's manifest file for examples). The tests can return a test
result object, which is then used to display results to the user. |
| R | Result panel plug-ins are arbitrary components that can
be displayed as a tab at the bottom of the screen. Examples include
the "Find Usage" results, the classification output, and
the ontology test results. You must subclass ResultsPanel and can
then use some standard services such as selecting a highlighted
object from there. You can add or remove your result panels as a
result of some action using the ResultsPanelManager. |
| C | Conditions widget extension plug-ins can be installed
by a project plug-in to add additional tabs to the conditions widget.
This is currently in its infancy, but you can call ConditionsWidget.addNestedWidgetFactory
to add your panel (e.g., to display the abstract syntax). |
You can use any of the previously described Protégé-OWL API features for stand-alone applications. Such applications can load ontologies from the Semantic Web, perform queries on them, add or edit resources from the ontology, classify instances and classes, and then write out resulting ontologies to a file. Some thoughts on the architecture and development methodology for Semantic Web applications were published in a recent paper. The following figure, taken from this publication, illustrates the typical architecture of such an application:

Semantic Web applications usually need to make some ontological commitments,
i.e., they need to have hard-coded knowledge about a certain domain ontology.
In the example above, the application has hard-coded behavior that depends
on the travel.owl ontology, which contains classes like Activity and
Destination. The application can also operate on extensions of these
core concepts, e.g., stemming from dynamic extension ontologies about
specific types of activities and destinations. Then, the application
can exploit reasoning engines like Racer or rule engines like SWRL to expose
"intelligent" behavior. All of this is controlled by some logic
(in this example it is Java code), which also interacts with the end user by means of
interface technologies like JSPs, Swing applications, or Web Services.
For most such scenarios, it quickly becomes inconvenient to handle ontology
concepts using only generic APIs such as Protégé-OWL or Jena. Let us look
at a scenario that illustrates this point. Assume we have a simple
domain model consisting of Products, Customers, and Purchases.
Products have a price,
Customers have firstName, lastName, zipCode, and
a collection of purchases.
Each Purchase consists of one product,
one customer, and a date.

The OWL ontology to represent such a domain model is shown below. It
is easy to refine this model to be somehow more interesting. For example,
we can add a class GoodCustomer, defined as those customers that
have a certain number of purchases in their record.
<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns="http://www.owl-ontologies.com/javaDemo.owl#"
xml:base="http://www.owl-ontologies.com/javaDemo.owl">
<owl:Ontology rdf:about=""/>
<owl:Class rdf:ID="VeryGoodCustomer">
<owl:equivalentClass>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:ID="Customer"/>
<owl:Restriction>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>100</owl:minCardinality>
<owl:onProperty>
<owl:ObjectProperty rdf:ID="purchases"/>
</owl:onProperty>
</owl:Restriction>
</owl:intersectionOf>
</owl:Class>
</owl:equivalentClass>
</owl:Class>
<owl:Class rdf:ID="GoodCustomer">
<owl:equivalentClass>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:about="#Customer"/>
<owl:Restriction>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>2</owl:minCardinality>
<owl:onProperty>
<owl:ObjectProperty rdf:about="#purchases"/>
</owl:onProperty>
</owl:Restriction>
</owl:intersectionOf>
</owl:Class>
</owl:equivalentClass>
</owl:Class>
<owl:Class rdf:ID="Product"/>
<owl:Class rdf:ID="Purchase">
<rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty>
<owl:FunctionalProperty rdf:ID="product"/>
</owl:onProperty>
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>1</owl:cardinality>
<owl:onProperty>
<owl:FunctionalProperty rdf:ID="customer"/>
</owl:onProperty>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"
>1</owl:cardinality>
<owl:onProperty>
<owl:FunctionalProperty rdf:ID="date"/>
</owl:onProperty>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
<owl:ObjectProperty rdf:about="#purchases">
<owl:inverseOf>
<owl:FunctionalProperty rdf:about="#customer"/>
</owl:inverseOf>
<rdfs:range rdf:resource="#Purchase"/>
<rdfs:domain rdf:resource="#Customer"/>
</owl:ObjectProperty>
<owl:DatatypeProperty rdf:ID="lastName">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
<rdfs:domain rdf:resource="#Customer"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="zipCode">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#int"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
<rdfs:domain rdf:resource="#Customer"/>
</owl:DatatypeProperty>
<owl:FunctionalProperty rdf:ID="price">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#float"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#DatatypeProperty"/>
<rdfs:domain rdf:resource="#Product"/>
</owl:FunctionalProperty>
<owl:FunctionalProperty rdf:about="#product">
<rdfs:domain rdf:resource="#Purchase"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
<rdfs:range rdf:resource="#Product"/>
</owl:FunctionalProperty>
<owl:FunctionalProperty rdf:ID="firstName">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#DatatypeProperty"/>
<rdfs:domain rdf:resource="#Customer"/>
</owl:FunctionalProperty>
<owl:FunctionalProperty rdf:about="#date">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#date"/>
<rdfs:domain rdf:resource="#Purchase"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#DatatypeProperty"/>
</owl:FunctionalProperty>
<owl:FunctionalProperty rdf:about="#customer">
<rdfs:domain rdf:resource="#Purchase"/>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
<owl:inverseOf rdf:resource="#purchases"/>
<rdfs:range rdf:resource="#Customer"/>
</owl:FunctionalProperty>
</rdf:RDF>
Assuming we want to build a Java application around this model, we need to access the objects in the ontology and the run-time objects, e.g., the individual products and customers. Some generic code to accomplish this is as follows:
OWLNamedClass customerClass = owlModel.getOWLNamedClass("Customer");
OWLDatatypeProperty firstNameProperty = owlModel.getOWLDatatypeProperty("firstName");
OWLIndividual myCustomer = owlModel.getOWLIndividual("Customer42");
String firstName = (String) myCustomer.getPropertyValue(firstNameProperty);
Such code has several weaknesses. Among other things, it is dangerous to operate only on ontological resources in terms of their names as strings, and generic code quickly becomes hard to read. The following method, for example, calculates the sum of all purchases of a given customer:
private static float getPurchasesSum(RDFIndividual customer) {
OWLModel owlModel = customer.getOWLModel();
float sum = 0;
RDFProperty purchasesProperty = owlModel.getRDFProperty("purchases");
RDFProperty productProperty = owlModel.getRDFProperty("product");
RDFProperty priceProperty = owlModel.getRDFProperty("price");
Iterator purchases = customer.listPropertyValues(purchasesProperty);
while(purchases.hasNext()) {
RDFIndividual purchase = (RDFIndividual) purchases.next();
RDFIndividual product = (RDFIndividual) purchase.getPropertyValue(productProperty);
Float price = (Float) product.getPropertyValue(priceProperty);
sum += price.floatValue();
}
return sum;
}
From an object-oriented perspective, it would be much more convenient to have a class such as:
public interface Customer {
String getFirstName();
void setFirstName(String value);
...
float getPurchasesSum();
}
... so that we can use code like this:
Customer myCustomer = [somehow get Customer object from the OWLModel];
String firstName = myCustomer.getFirstName();
float sum = myCustomer.getPurchasesSum();
This is possible with the Protégé-OWL API. You need to make Customer a subinterface
of a suitable Protégé-OWL base interface like OWLIndividual:
package com.demo.model;
public interface Customer extends OWLIndividual {
String getFirstName();
void setFirstName(String value);
...
}
... and then provide a default implementation like the following scheme:
package com.demo.model.impl;
public class DefaultCustomer extends DefaultOWLIndividual implements Customer {
public DefaultCustomer(KnowledgeBase kb, FrameID id) {
super(kb, id);
}
public String getFirstName() {
RDFProperty property = getOWLModel().getRDFProperty("firstName");
return (String) getPropertyValue(property);
}
public void setFirstName(String value) {
RDFProperty property = getOWLModel().getRDFProperty("firstName");
setPropertyValue(property, value);
}
...
}
Note that these interfaces and their implementations can be easily generated using a code generator (which is in fact available for the Protégé-OWL editor). If these classes are part of the project's classpath, then we can already use the following call to "typecast" generic resources into convenience classes:
Customer myCustomer = (Customer) owlModel.getRDFResource("Customer42").as(Customer.class);
String firstName = myCustomer.getFirstName();
The key here is the RDFResource.as()
method, which allows programmers to
get a different "view" of a given resource. The method
RDFResource.canAs()
can be used to test whether a given object can be cast into a given interface.
This mechanism allows programmers to build true object-oriented programs
out of OWL ontologies. It is possible to add methods to the
generated Java interfaces and implementations. In the following example,
the interface Customer_ and
its implementation DefaultCustomer_ are
generated automatically, while the subclasses
Customer and DefaultCustomer contain
methods added by a programmer. The separation into generated classes and
hand-coded classes allows programmers to overwrite the old classes without
breaking anything.
package edu.stanford.smi.protegex.owlx.examples.javaDemo.model; import edu.stanford.smi.protegex.owl.model.RDFIndividual; import edu.stanford.smi.protegex.owl.model.RDFProperty; import java.util.Collection; import java.util.Iterator; /** * Generated by Protégé-OWL (http://protege.stanford.edu/plugins/owl). * Source OWL Class: http://www.owl-ontologies.com/javaDemo.owl#Customer * * @version generated on Sat Feb 19 17:10:28 EST 2005 */ public interface Customer_ extends OWLIndividual { // Property http://www.owl-ontologies.com/javaDemo.owl#firstName String getFirstName(); RDFProperty getFirstNameProperty(); boolean hasFirstName(); void setFirstName(String newFirstName); // Property http://www.owl-ontologies.com/javaDemo.owl#lastName String getLastName(); RDFProperty getLastNameProperty(); boolean hasLastName(); void setLastName(String newLastName); // Property http://www.owl-ontologies.com/javaDemo.owl#purchases Collection getPurchases(); RDFProperty getPurchasesProperty(); boolean hasPurchases(); Iterator listPurchases(); void addPurchases(Purchase newPurchases); void removePurchases(Purchase oldPurchases); void setPurchases(Collection newPurchases); // Property http://www.owl-ontologies.com/javaDemo.owl#zipCode int getZipCode(); RDFProperty getZipCodeProperty(); boolean hasZipCode(); void setZipCode(int newZipCode); } ----- package edu.stanford.smi.protegex.owlx.examples.javaDemo.model.impl; import edu.stanford.smi.protege.model.FrameID; import edu.stanford.smi.protegex.owl.model.RDFProperty; import edu.stanford.smi.protegex.owl.model.impl.DefaultRDFIndividual; import edu.stanford.smi.protegex.owlx.examples.javaDemo.model.Customer_; import edu.stanford.smi.protegex.owlx.examples.javaDemo.model.Purchase; import java.util.Collection; import java.util.Iterator; /** * Generated by Protégé-OWL (http://protege.stanford.edu/plugins/owl). * Source OWL Class: http://www.owl-ontologies.com/javaDemo.owl#Customer * * @version generated on Sat Feb 19 17:10:28 EST 2005 */ public class DefaultCustomer_ extends DefaultRDFIndividual implements Customer_ { public DefaultCustomer_(OWLModel owlModel, FrameID id) { super(owlModel, id); } public DefaultCustomer_() { } // Property http://www.owl-ontologies.com/javaDemo.owl#firstName public String getFirstName() { return (String) getPropertyValue(getFirstNameProperty()); } public RDFProperty getFirstNameProperty() { final String uri = "http://www.owl-ontologies.com/javaDemo.owl#firstName"; final String name = getOWLModel().getResourceNameForURI(uri); return getOWLModel().getRDFProperty(name); } public boolean hasFirstName() { return getPropertyValueCount(getFirstNameProperty()) > 0; } public void setFirstName(String newFirstName) { setPropertyValue(getFirstNameProperty(), newFirstName); } // Property http://www.owl-ontologies.com/javaDemo.owl#lastName public String getLastName() { return (String) getPropertyValue(getLastNameProperty()); } public RDFProperty getLastNameProperty() { final String uri = "http://www.owl-ontologies.com/javaDemo.owl#lastName"; final String name = getOWLModel().getResourceNameForURI(uri); return getOWLModel().getRDFProperty(name); } public boolean hasLastName() { return getPropertyValueCount(getLastNameProperty()) > 0; } public void setLastName(String newLastName) { setPropertyValue(getLastNameProperty(), newLastName); } // Property http://www.owl-ontologies.com/javaDemo.owl#purchases public Collection getPurchases() { return getPropertyValuesAs(getPurchasesProperty(), Purchase.class); } public RDFProperty getPurchasesProperty() { final String uri = "http://www.owl-ontologies.com/javaDemo.owl#purchases"; final String name = getOWLModel().getResourceNameForURI(uri); return getOWLModel().getRDFProperty(name); } public boolean hasPurchases() { return getPropertyValueCount(getPurchasesProperty()) > 0; } public Iterator listPurchases() { return listPropertyValuesAs(getPurchasesProperty(), Purchase.class); } public void addPurchases(Purchase newPurchases) { addPropertyValue(getPurchasesProperty(), newPurchases); } public void removePurchases(Purchase oldPurchases) { removePropertyValue(getPurchasesProperty(), oldPurchases); } public void setPurchases(Collection newPurchases) { setPropertyValues(getPurchasesProperty(), newPurchases); } // Property http://www.owl-ontologies.com/javaDemo.owl#zipCode public int getZipCode() { return getPropertyValueLiteral(getZipCodeProperty()).getInt(); } public RDFProperty getZipCodeProperty() { final String uri = "http://www.owl-ontologies.com/javaDemo.owl#zipCode"; final String name = getOWLModel().getResourceNameForURI(uri); return getOWLModel().getRDFProperty(name); } public boolean hasZipCode() { return getPropertyValueCount(getZipCodeProperty()) > 0; } public void setZipCode(int newZipCode) { setPropertyValue(getZipCodeProperty(), new Integer(newZipCode)); } } ----- package edu.stanford.smi.protegex.owlx.examples.javaDemo.model; /** * An extension of Customer_ with extra methods. */ public interface Customer extends Customer_ { /** * Computes the sum of all products that were purchased by this Customer. * @return the sum of all purchases */ public float getPurchasesSum(); } ----- package edu.stanford.smi.protegex.owlx.examples.javaDemo.model.impl; import edu.stanford.smi.protege.model.FrameID; import edu.stanford.smi.protegex.owlx.examples.javaDemo.model.Customer; import edu.stanford.smi.protegex.owlx.examples.javaDemo.model.Product; import edu.stanford.smi.protegex.owlx.examples.javaDemo.model.Purchase; import java.util.Iterator; public class DefaultCustomer extends DefaultCustomer_ implements Customer { public DefaultCustomer(OWLModel owlModel, FrameID id) { super(owlModel, id); } public DefaultCustomer() { } public String getBrowserText() { return getFirstName() + " " + getLastName(); } public float getPurchasesSum() { float sum = 0; Iterator purchases = listPurchases(); while (purchases.hasNext()) { Purchase purchase = (Purchase) purchases.next(); Product product = purchase.getProduct(); sum += product.getPrice(); } return sum; } }
Note that although the implementation classes have constructors, instances
of these classes can only be created indirectly, through the OWLModel.
For that purpose, the code generator also creates a "factory" class
that can be used to create new objects in the OWLModel:
package edu.stanford.smi.protegex.owlx.examples.javaDemo.model;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.RDFSNamedClass;
/**
* Generated by Protégé-OWL (http://protege.stanford.edu/plugins/owl).
*
* @version generated on Mon Feb 21 10:53:08 EST 2005
*/
public class MyFactory {
private OWLModel owlModel;
public MyFactory(OWLModel owlModel) {
this.owlModel = owlModel;
}
public RDFSNamedClass getCustomerClass() {
final String uri = "http://www.owl-ontologies.com/javaDemo.owl#Customer";
final String name = owlModel.getResourceNameForURI(uri);
return owlModel.getRDFSNamedClass(name);
}
public Customer createCustomer(String name) {
final RDFSNamedClass cls = getCustomerClass();
return (Customer) cls.createInstance(name).as(Customer.class);
}
public RDFSNamedClass getPurchaseClass() {
final String uri = "http://www.owl-ontologies.com/javaDemo.owl#Purchase";
final String name = owlModel.getResourceNameForURI(uri);
return owlModel.getRDFSNamedClass(name);
}
public Purchase createPurchase(String name) {
final RDFSNamedClass cls = getPurchaseClass();
return (Purchase) cls.createInstance(name).as(Purchase.class);
}
public RDFSNamedClass getProductClass() {
final String uri = "http://www.owl-ontologies.com/javaDemo.owl#Product";
final String name = owlModel.getResourceNameForURI(uri);
return owlModel.getRDFSNamedClass(name);
}
public Product createProduct(String name) {
final RDFSNamedClass cls = getProductClass();
return (Product) cls.createInstance(name).as(Product.class);
}
}
It then becomes straight-forward to create, modify, and query the ontological resources using their object-oriented convenience classes.
private static Purchase createPurchase(Customer customer, Product product, String date) {
OWLModel owlModel = customer.getOWLModel();
Purchase purchase = new MyFactory(owlModel).createPurchase(null);
purchase.setCustomer(customer);
purchase.setProduct(product);
RDFSDatatype xsdDate = owlModel.getRDFSDatatypeByName("xsd:date");
purchase.setDate(owlModel.createRDFSLiteral(date, xsdDate);
return purchase;
}