ProtegeOWL API Programmers Guide

From Protege Wiki

Revision as of 10:32, June 28, 2010 by Rahul.doraiswami (Talk | contribs)

Jump to: navigation, search

Protege-OWL API Programmer's Guide

The Protege-OWL API is an open-source Java library for the Web Ontology Language (OWL) and RDF(S). The API provides classes and methods to load and save OWL files, to query and manipulate OWL data models, and to perform reasoning based on Description Logic engines. Furthermore, the API is optimized for the implementation of graphical user interfaces.

- Original Author: Holger Knublauch
- Currently maintained by: Protege staff members (contributions from the user community are welcome)
- Last update: May 13, 2010


Contents


Overview

The Protege-OWL API is an open-source Java library for the Web Ontology Language (OWL) and RDF(S). The API provides classes and methods to load and save OWL files, to query and manipulate OWL data models, and to perform reasoning based on Description Logic engines. Furthermore, the API is optimized for the implementation of graphical user interfaces.

The API is designed to be used in two contexts:

  • For the development of components that are executed inside of the Protege-OWL editor's user interface
  • For the development of stand-alone applications (e.g., Swing applications, Servlets, or Eclipse plug-ins)

Protege is a flexible, configurable platform for the development of arbitrary model-driven applications and components. Protege has an open architecture that allows programmers to integrate plug-ins, which can appear as separate tabs, specific user interface components (widgets), or perform any other task on the current model. The Protege-OWL editor provides many editing and browsing facilities for OWL models, and therefore can serve as an attractive starting point for rapid application development. Developers can initially wrap their components into a Protege tab widget and later extract them to distribute them as part of a stand-alone application.

This guide will introduce you to some basic concepts of the Protege-OWL API, no matter whether you intend to use it for stand-alone applications or Protege plug-ins. The guide tries to illustrate the API by examples, and most examples create parts of OWL ontologies such as properties and restrictions. The complete source code for most of the examples can be found in the edu.stanford.smi.protegex.owlx.examples package in the Protege-OWL source code. If you have trouble getting started, please post questions on the protege-owl mailing list.

Installation & Getting Started

The Protege-OWL editor is provided with the standard installation of Protege. In addition to installing Protege, you should download the source code from the Protege Subversion repository. The source code is the most reliable reference for the system's functionality and browsing the source code with a Java IDE is a great way to learn the API. Detailed instructions on how to download source code from our repository are available on the main Protege website. The URL to download the Protege-OWL source code using a Subversion client is:

http://smi-protege.stanford.edu/repos/protege/owl/trunk/.

You will also find Javadoc for the API useful. In particular, you will need to understand the interfaces from the edu.stanford.smi.protegex.owl.model package.

Following a successful installation, your directory structure should look approximately as follows:

Directory structure of the Protege application after a successful install

Now let us configure our Java project and write a small "Hello World" application. If you are using a Java IDE such as Eclipse or IntelliJ, select the Protege installation folder as your project home. Next, add all the JAR files from the installation to your project classpath. Set your compiler output path to plugins/classes.

Then create a Java class such as the following:

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());
    }
}

Execute this program stand-alone. The output should be "Class URI: http://hello.com#World".

Basics

Working with OWL Models

The Protege-OWL API is centered around a collection of Java interfaces from the 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 Protege 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);

Names, Namespace prefixes, and URIs

OWL and RDF resources are globally identified by their URIs, such as 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 Protege-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.

Understanding the Model Interfaces

The interfaces of the model package are arranged in an inheritance hierarchy. An overview of the available interfaces can be found in the API diagram, contributed by Matthew Horridge. (You may want to print this diagram and leave it on your desk while you are using the API.) The base interface of all resources is RDFResource, from which subinterfaces for classes, properties, and individuals are derived:

 RDFResource
     RDFSClass
     RDFProperty
     RDFIndividual

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));

Creating Named Classes and Individuals

The Protege-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();

Using Datatype Properties and Datatype Values

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"));

To summarize, datatype values are handled either as primitive objects (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().

Using Object Properties to Build Relationships between Resources

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 Protege-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 Resources

In 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 Protege-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());

Property Domains

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 Protege-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));

Advanced Class Definitions (OWL DL)

Restrictions

The Protege-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 Protege-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 (Unions, Intersections, Complements)

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

Enumerated Classes

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.

Creating Defined Classes

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 Protege-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);

The important call here is OWLNamedClass.setDefinition(), which declares the named class to be equivalent to the anonymous class. Following this call, the class Person is also 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.

Advanced Topics

Querying the OWLModel

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.

RDF(S) and OWL

The Protege-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.

Reacting to Changes using Listeners

The Protege-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 Files

The Protégé-OWL API can be used in two storage modes:

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.");

Working with Multi-File Projects and TripleStores

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 models

Proté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());

As an application programmer, you are free to do arbitrary operations on the resulting 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.

Personal tools