Writing a Simple Plugin in Protégé 4.0

Writing plugins for Protégé 4.0 alpha is very straightforward once you have a build environment up and running. There are three parts to a simple plugin, a java implementation of one of the abstract plugin classes (e.g. AbstractOWLClassViewComponent), an xml file that describes the plugin, and a manifest file that describes the plugin dependencies.

The API and plugin mechanism makes for very easy and flexible plugin implementation, meaning that it is very straightforward putting together views, menu actions and tabs (and possible through elipse's equinox on which Protégé sits to create new plugin types).

Simple Example - try out the view in Protégé first

We introduce a simple example - implementation of a view plugin that renders the subclasses of the selected class as a tab indented list. The code is available as a compiled plugin.

To see it in action

Get the source

You can develop straight from the distribution jars (actually you will have to unzip the dependant plugins - org.editor.protege.owl, org.protege.editor.core.application and org.semanticweb.owl.owlapi), but if prefered, source code is available from the Protégé Subversion Repository. Setup of your IDE in this case is individual to your environment, and is outside the scope of this introduction.
There is a more comprehensive set of instructions for Eclipse available - building Protégé 4.0.

Write some code

At the minimum, you need to set up your classpath for compilation to include the plugins above (including any jar in each of their lib/ folders. Then you are ready to start coding - the code for this view is below. To extend AbstractOWLClassViewComponent you must implement the following:

The most useful method on the plugin getOWLModelManager() gives you access to the hub of the Protégé framework. The OWLModelManager provides access to the ontologies, expression parsers, renderers, reasoners, change management, events, and serialisation. For user interface related access to the framework, getOWLWorkspace() returns the GUI, providing access to the selection model, tabs, results panel etc.

package org.coode.taxonomy;

import org.protege.editor.owl.ui.view.AbstractOWLClassViewComponent;
import org.protege.editor.owl.ui.renderer.OWLModelManagerEntityRenderer;
import org.protege.editor.owl.model.hierarchy.OWLObjectHierarchyProvider;
import org.semanticweb.owl.model.OWLClass;

import javax.swing.*;
import java.awt.*;

/**
 * Author: Nick Drummond
 * http://www.cs.man.ac.uk/~drummond
 * 
 * The University Of Manchester
 * Bio Health Informatics Group
 * Date: Jul 10, 2007
 * 
 * 
 * Shows a tab indented tree of descendants for the selected class
 */
 
public class TabbedHierarchyView extends AbstractOWLClassViewComponent {

    private JTextArea namesComponent;

    // convenience class for querying the asserted subsumption hierarchy directly
    private OWLObjectHierarchyProvider<OWLClass> assertedHierarchyProvider;

    // provides string renderings of Classes/Properties/Individuals, reflecting the current output settings
    private OWLModelManagerEntityRenderer ren;

    // create the GUI
    public void initialiseClassView() throws Exception {
        setLayout(new BorderLayout(6, 6));
        // in our implementation, just create a simple text area in a scrollpane
        namesComponent = new JTextArea();
        namesComponent.setTabSize(2);
        add(new JScrollPane(namesComponent), BorderLayout.CENTER);
    }

    // called automatically when the global selection changes
    protected OWLClass updateView(OWLClass selectedClass) {
        namesComponent.setText("");
        if (selectedClass != null){
            assertedHierarchyProvider = getOWLModelManager().getOWLClassHierarchyProvider();
            ren = getOWLModelManager().getOWLEntityRenderer();
            render(selectedClass, 0);
        }
        return selectedClass;
    }

    // render the class and recursively all of its subclasses
    private void render(OWLClass selectedClass, int indent) {
        for (int i=0; i<indent; i++){
            namesComponent.append("\t");
        }
        namesComponent.append(ren.render(selectedClass));
        namesComponent.append("\n");
        // the hierarchy provider gets subclasses for us
        for (OWLClass sub: assertedHierarchyProvider.getChildren(selectedClass)){
            render(sub, indent+1);
        }
    }

    // remove any listeners and perform tidyup (none required in this case)
    public void disposeView() {
    }
}

Specify the plugin

The second part of the process is to describe the plugin so that Protégé can find it. You will need to create a plugins.xml file pretty similar to that below. This file describes the plugin(s), with an identifier, a label, a pointer to the java class implementation and then some setup specific to the view plugin (like where it is in the menu, and what colour its header is).

  <?xml version="1.0" ?>
  <plugin>
  	<extension id="org.coode.taxonomy.TabbedHierarchyView"
			point="org.protege.editor.core.application.ViewComponent">
  		<label value="Tabbed Subclasses"/>
  		<class value="org.coode.taxonomy.TabbedHierarchyView"/>
  		<headerColor value="@org.protege.classcolor"/>
  		<category value="@org.protege.classcategory"/>
  	</extension>
  </plugin>
  

Specify the manifest

The manifest file sets up the dependencies between plugins (the OWL editor kit is itself a plugin that we need to include), sets further paths (local). You will need to create a MANIFEST.MF file in a directory called META-INF in your plugin.

Warning! Do not cut and paste this code as manifest files appear to be very sensitive to whitespace (as we have found after extreme pain).

  Manifest-Version: 1.0
  Bundle-ManifestVersion: 2
  Bundle-Name: Taxonomy Example View
  Bundle-SymbolicName: org.coode.taxonomy;singleton:=true
  Bundle-Category: protege
  Bundle-Description: An example view to demonstrate the plugin mechanism for developers
  Bundle-Vendor: The CO-ODE Group
  Bundle-DocURL: http://www.co-ode.org
  Bundle-ClassPath: .
  Import-Package: org.osgi.framework,
                  org.apache.log4j
  Bundle-Version: 1.0.0
  Bundle-Activator: org.protege.editor.core.plugin.DefaultPluginActivator
  Require-Bundle: org.eclipse.equinox.registry,
  				  org.eclipse.equinox.common,
				  org.protege.editor.core.application,
				  org.protege.editor.owl,
				  org.semanticweb.owl.owlapi

Bundle the plugin and run

Make sure that you compile or copy the class packages and the plugins.xml file into a suitable folder in the plugins/ directory in your Protégé installation (The src and update.xml are not required). Make sure the manifest is also included in its META-INF directory. The final plugin structure should look like this:

Run Protégé, and check that your plugin has been picked up correctly.

Read up on the technology

The OWL editor kit is implemented on top of the Wonderweb OWL API. Many of the peripheral details like loading and saving, reasoner access etc are dealt with using the OWLModelManager but you will use the API for the ontology entities themselves. A short introduction to the design decisions (including use of the Visitor pattern) are available at the OWL API pages.

The Eclipse Equinox OSGi framework is used to handle the plugin mechanisms within Protégé and is flexible enough to allow you to create new plugin types. Read more about equinox here.