ProgrammingWithProtegeOWL

From Protege Wiki
Revision as of 12:53, July 1, 2010 by JenniferVendetti (talk | contribs) (moved ProgrammingWithProtege to ProgrammingWithProtegeOWL: Needed better page name)

Jump to: navigation, search

Main article: ProtegeOWL_API_Programmers_Guide



Programming with Protege

Protege-OWL and the Core Protege API

The Protege-OWL API is an extension of the core Protege API. For those users who are not familiar with Core Protege, there is a different mode of the system (collectively referred to as Core Protege and Protege-Frames) based on a metamodel called OKBC. This "core" mode predates the OWL mode of Protege 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 Protege-OWL. This means that Protege-OWL is technically an extension of the core Protege system. More information about the differences between Protégé-Frames and Protégé-OWL is available in the overview section of the main Protege website.

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


Changes from the old Protege-OWL API

If you have never used the old Protege-OWL API, you can skip this section.

In the new version of the Protege-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 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 Protege classes but the Protege details are hidden using a set of abstract base interfaces ProtegeCls, ProtegeSlot, ProtegeInstance and ProtegeKnowledgeBase, which declare almost all Protege methods deprecated.

If you are converting an old Protege-based Java program to the new Protege-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.

  • The notion of template slots in Protege is different in OWL because in OWL you typically work with a property-centric view, i.e., you don't assign a slot to a class, but add a class to the domain of a property. Also, if multiple classes are needed, then most users mean a unionOf. This is reflected with the term UnionDomain in several places in the API.
  • Core Protege has a fixed set of 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 Protege-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.
  • Since there is no equivalent to facets in OWL, no facet related methods can be used in Protege-OWL. There are internal mechanisms that create facet values from restrictions (and perhaps vice versa), but this code is not well-tested and would only be needed by legacy components.
  • Datatype properties no longer have strings as their default ranges.
  • Another important aspect is that the core Protege API makes a distinction between "direct" values and other values. Direct values were those values that appeared in direct association with the resource. The non-direct values are derived values via superclass or subproperty relationships. Reflecting this distinction, the old API had two types of methods, e.g., 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 Protege-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 Protege-OWL API, the following table lists the new names of frequently used methods:


Old Protege OWL API New Protege 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)


Protege User Interface Programming

The Protege 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 Protege family of tools.

One of the foundations of UI programming with Protege 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:


File-SimpleListExample.png


When the user hits the add button, a dialog to select the type of the new Destination is shown:


ClassSelectionDialog.png


After the user has selected a class, a small dialog asks for the name of the new Destination instance:


NameDialog.png


... and the new instance is added:


SimpleListExampleAdded.png


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 Protege library and can easily be reused, just like the OWLLabeledComponent class with the header button support. Many of the existing Protege components can be subclassed and thus serve as the foundation for completely new applications and Protege plug-ins.


Protege Plug-in Development

Protege 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 Protege-OWL editor. The Protege 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 Protege-OWL editor, e.g., OWLViz, OWLDoc, Protege 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 a dependency on the Protege-OWL editor.


Configuring-overview.png


T Tab widget plug-ins are a Core Protege 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 Protege 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 Protege 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 Protege-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.