ProtegeOWL API Advanced Topics

From Protege Wiki
Revision as of 11:40, February 15, 2011 by Taruss (talk | contribs) (Querying the OWLModel: Changed part of example for File URIs to be more robust.)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Main article: ProtegeOWL_API_Programmers_Guide



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. If you do this, it is more robust
//to go through the File object to get the URI instead of writing it by hand
// Example:
//String uri = new File("c:/Work/Projects/travel.owl").toURI();

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 Protege-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 Protege-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 Protege user interface allows users to switch the "active" sub-ontology, so that users can select which file changes will be propagated to.

Protege stores OWL ontologies in triple tables, one table for each file. These tables are 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

Protege-OWL uses the popular Jena API for various tasks, in particular for parsing of OWL/RDF files. Furthermore, the Protege-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.