Working with Perl Modules Representing OWL Classes

This document summarises the creation and utilization of Perl modules, generated with the sadi-generate-datatypes.pl script, that represent OWL classes and their associated properties.

The main thing to understand is that using these perl modules will make your life much more simple with regards to implementing your own SADI services. The generated classes extrapolate the OWL semantics and allow you to use any OWL class, datatype property or object property in an object oriented manner.

At the end of this tutorial, you should be able to generate perl modules from an OWL file and be able to use these modules in your service provision.

Let's now move on towards seeing how this all works.

Table of Contents

Quick Synopsis for the Impatient

For those of you that wish to see how to use the Perl modules without reading further (or to refresh your memory), the following is for you!

# perl modules generated using the following command:
# sadi-generate-datatypes.pl example.owl

# our use statements (for completion of this example)
use sadiframework::org::examples::example::AnnotatedGeneID_Record;
use ontology::dumontierlab::com::hasSymbol;
use sadiframework::org::ontologies::predicates::hasDescription;
use sadiframework::org::ontologies::predicates::hasProteinName;
use sadiframework::org::ontologies::predicates::hasName;

# instantiate a new record
my $class = new sadiframework::org::examples::example::AnnotatedGeneID_Record("http://some_uri/for/my/record");

# set a label
$class->label("this is my label for this record");

# get the label
my $label = $class->label();

# get the the uri for this record
my $uri = $class->uri();

# add some symbols to this record (datatype property)
$class->add_hasSymbol(ontology::dumontierlab::com::hasSymbol->new('Some_Gene_Symbol'));
$class->add_hasSymbol(ontology::dumontierlab::com::hasSymbol->new('Another_Gene_Symbol'));

# you can still get/set hasDescription(), hasProteinName() and hasName() (all are datatype properties in this example)

# for imaginary hasReference() object property described here
$class->add_hasReference(new sadiframework::org::examples::example::AnnotatedGeneID_Record("http://lsrn.org/Gene:642"));

Prerequisites

Of course, the main prerequisite is that you have installed SADISeS on your machine. SADISeS can be found on CPAN. When you install this Perl module onto your computer, you will also be prompted to install any missing Perl modules required by SADISeS. After installation, (i.e. make install), you are required to also run a SADI installation script, sadi-install.pl. This is only done the first time that you install SADISeS (and sometimes when you upgrading your SADISeS codebase; Looking at the README or CHANGES will make it obvious as to when this is required).

Another prerequiste for generating Perl modules from OWL is an OWL document! In this tutorial, we will present all OWL that we use for you to download.

Generating Perl Modules from OWL Classes

The generation of Perl modules representing OWL entities is done by the sadi-generate-datatypes.pl script, from now on refered to as 'the script', that is installed onto your computer when you install the SADI perl module.

If you run the script without arguments, you are shown something like:

Generate perl modules from OWL files.
Usage: [-vdsib] owl-class-file
       [-vdsi] -u owl-class-url

    -u ... owl is from url
    -s ... show generated code on STDOUT
           (no file is created, disabled when no data type name given)

    -b ... option to specify the base uri for the owl document (you will be prompted)

    -i ... follow owl import statements

    -v ... verbose
    -d ... debug
    -h ... help

Note: This script requires that the PERL module ODO, from IBM Semantic Layered
      Research Platform be installed on your workstation! ODO is available on CPAN
      under the name PLUTO.

As you can see, the script consumes either a file or url that points to a file containing the OWL entities that you wish to generate Perl modules. Many OWL ontologies import concepts from other files and so the script also allows you to follow these import statements. Additionally for your convenience, the script also allows for showing generated code on STDOUT.

For the rest of this document, the following OWL ontology will be used:

<?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#"
    xml:base="http://sadiframework.org/examples/example.owl#">
  <owl:Ontology rdf:about=""/>
  
  <!-- in lieu of importing the whole ontology... -->
  <owl:DatatypeProperty rdf:about="http://ontology.dumontierlab.com/hasSymbol"/>
  <owl:DatatypeProperty rdf:about="http://sadiframework.org/ontologies/predicates.owl#hasDescription"/>
  <owl:DatatypeProperty rdf:about="http://sadiframework.org/ontologies/predicates.owl#hasProteinName"/>
  <owl:DatatypeProperty rdf:about="http://sadiframework.org/ontologies/predicates.owl#hasName"/>
  <owl:Class rdf:about="http://purl.oclc.org/SADI/LSRN/GeneID_Record"/>
  
  <owl:Class rdf:ID="AnnotatedGeneID_Record">
    <rdfs:subClassOf rdf:resource="http://purl.oclc.org/SADI/LSRN/GeneID_Record"/>
    <rdfs:subClassOf>
      <owl:Class>
        <owl:intersectionOf rdf:parseType="Collection">
          <owl:Restriction>
            <owl:onProperty rdf:resource="http://ontology.dumontierlab.com/hasSymbol"/>
            <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">0</owl:minCardinality>
          </owl:Restriction>
          <owl:Restriction>
            <owl:onProperty rdf:resource="http://sadiframework.org/ontologies/predicates.owl#hasDescription"/>
            <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">0</owl:minCardinality>
          </owl:Restriction>
          <owl:Restriction>
            <owl:onProperty rdf:resource="http://sadiframework.org/ontologies/predicates.owl#hasProteinName"/>
            <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">0</owl:minCardinality>
          </owl:Restriction>
          <owl:Restriction>
            <owl:onProperty rdf:resource="http://sadiframework.org/ontologies/predicates.owl#hasName"/>
            <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">0</owl:minCardinality>
          </owl:Restriction>
        </owl:intersectionOf>
      </owl:Class>
    </rdfs:subClassOf>
  </owl:Class>
</rdf:RDF>

Please copy and paste the above document into a local file as we will be generating Perl modules from this ontology. Save the document as "example.owl".

To begin, issue the following command at the command prompt:

sadi-generate-datatypes.pl example.owl

You should then see something along the lines of:

Generating perl modules for: example.owl
Parsing schema file: example.owl
Done.

What has happened is that the ontology described above had been parsed and Perl Modules for AnnotatedGeneID_Record (an owl class), hasSymbol, hasName, hasProteinName and hasDescription (all of which are datatype properties) were generated. As well, there was an anonymous class that was generated.

One obvious question is "where are these perl modules being generated"?

You can always determine this after generation by looking in the services.log log file - the message has the INFO level which means it is almost always logged. But, if you want to know in advance here are the rules:

Please take some time to investigate the generated files. In the next section, we will explain how we can use these generated modules.

Using Perl Modules Representing OWL Classes

Every generated module that represents an OWL Class ultimately inherits from SADI::Data::OWL::Class. This class defines the methods:

      1. label - an RDF label for the class,
      2. uri/value - the URI for the entity, and
      3. type - the RDF:type of this entity.

All of these methods can be invoked with or without arguments. When invoked without arguments, the methods act as getters. And when a scalar value is used as an argument, the method acts as a setter.

# instantiate a new record
my $class = new sadiframework::org::examples::example::AnnotatedGeneID_Record("http://some_uri/for/my/record");

# set a label
$class->label("this is my label for this record");

# get the label
my $label = $class->label();

# get the the uri for this record
my $uri = $class->uri();

OWL properties are similar to their OWL Class counterparts mentioned above. Datatype properties inherit from SADI::Data::OWL::DatatypeProperty, while object properties inherit from SADI::Data::OWL::ObjectProperty.

SADI::Data::OWL::DatatypeProperty defines the following methods:

      1. value - the value for the property of interest,
      2. domain - the domain of this datatype property,
      3. range - the range of this datatype property, and
      4. uri - the type of this datatype property.

while SADI::Data::OWL::ObjectProperty defines the following, similarly named, methods:

      1. value - the URI for the subject of this object property,
      2. domain - the domain of this property, and
      3. range - the range for this property.

Again, all of these methods can be invoked with or without arguments. When invoked without arguments, the methods act as getters. And when a scalar value is used as an argument, the method acts as a setter.

Some OWL classes, like AnnotatedGeneID_Record, their inherited methods from SADI::Data::OWL::Class, as well as the ability to get/set their own object/datatype properties. For instance, members of the the OWL class AnnotatedGeneID_Record have zero or more of the the following datatype properties: hasSymbol, hasDescription, hasName, hasProteinName. The generated Perl module for this OWL class also has the ability to get/set these properties. How you ask? Take a look at the following Perl snippet (yours make look similar) for AnnotatedGeneID_Record:

@ISA = qw(
 SADI::Data::OWL::Class
 Blank::genid4b2bb18702dc 
);

Notice that our class has @ISA of SADI::Data::OWL::Class (you already knew this!) and Blank::genid4b2bb18702dc. This is generated file for an anonymous OWLclass. If we examine the source for this class, we see the following:

#-----------------------------------------------------------------
# Blank::genid4b2bb18702dc
# Generated: 18-Dec-2009 08:44:56 PST
# Contact: Edward Kawas <edward.kawas+sadi@gmail.com>
#-----------------------------------------------------------------
package Blank::genid4b2bb18702dc;

#use SADI::Base;

use SADI::Data::OWL::Class;


no strict;
use vars qw( @ISA );
@ISA = qw(
 SADI::Data::OWL::Class 
);
use strict;

# imports 
use SADI::Data::String;
use SADI::Data::Integer;
use SADI::Data::Float;
use SADI::Data::Boolean;
use SADI::Data::DateTime;



use ontology::dumontierlab::com::hasSymbol;
use sadiframework::org::ontologies::predicates::hasDescription;
use sadiframework::org::ontologies::predicates::hasProteinName;
use sadiframework::org::ontologies::predicates::hasName;

{
    my %_allowed = (
    
    
        hasSymbol => {
            type => 'ontology::dumontierlab::com::hasSymbol',
            is_array => 1, },
    
        hasDescription => {
            type => 'sadiframework::org::ontologies::predicates::hasDescription',
            is_array => 1, },
    
        hasProteinName => {
            type => 'sadiframework::org::ontologies::predicates::hasProteinName',
            is_array => 1, },
    
        hasName => {
            type => 'sadiframework::org::ontologies::predicates::hasName',
            is_array => 1, },
    
    );

...

Notice the methods hasSymbol, hasName, hasProteinName, and hasDescription. Each of these methods corresponds to a datatype property that members of the OWL class AnnotatedGeneID_Records can have values for. Additionally, you will notice that each method has 'type' information associated with it. When using the generated Perl OWL classes, if you try to incorrectly set a value of a property with an incorrect type, an exception will be raised.

Now you might ask yourself, how do I actually use these methods? That's simple! If you only need to set one value for the datatype property, then you simple call the method, e.g. hasSymbol(), and provide the correctly typed argument.

$class->hasSymbol(ontology::dumontierlab::com::hasSymbol->new('Some_Gene_Symbol'));

However, if you are required to set multiple values, prepend add_to the method.

$class->add_hasSymbol(ontology::dumontierlab::com::hasSymbol->new('Some_Gene_Symbol'));
$class->add_hasSymbol(ontology::dumontierlab::com::hasSymbol->new('Another_Gene_Symbol'));

All datatype properties use this convention.

Object properties are a bit different. Imagine for a second that our instances of our class AnnotatedGeneID_Record also had an object property called hasReference. Then the generated source might look something like:

#-----------------------------------------------------------------
# Blank::genid4b2bb18702dc
# Generated: 18-Dec-2009 08:44:56 PST
# Contact: Edward Kawas <edward.kawas+sadi@gmail.com>
#-----------------------------------------------------------------
package Blank::genid4b2bb18702dc;

#use SADI::Base;

use SADI::Data::OWL::Class;


no strict;
use vars qw( @ISA );
@ISA = qw(
 SADI::Data::OWL::Class 
);
use strict;

# imports 
use SADI::Data::String;
use SADI::Data::Integer;
use SADI::Data::Float;
use SADI::Data::Boolean;
use SADI::Data::DateTime;

use ontology::dumontierlab::com::hasSymbol;
use ontology::dumontierlab::com::hasReference;
use sadiframework::org::ontologies::predicates::hasDescription;
use sadiframework::org::ontologies::predicates::hasProteinName;
use sadiframework::org::ontologies::predicates::hasName;

{
    my %_allowed = (
    
    
        hasSymbol => {
            type => 'ontology::dumontierlab::com::hasSymbol',
            is_array => 1, },
    
        hasDescription => {
            type => 'sadiframework::org::ontologies::predicates::hasDescription',
            is_array => 1, },
    
        hasProteinName => {
            type => 'sadiframework::org::ontologies::predicates::hasProteinName',
            is_array => 1, },
    
        hasName => {
            type => 'sadiframework::org::ontologies::predicates::hasName',
            is_array => 1, },

         hasReference => {
            type => 'SADI::Data::OWL::Class',
            # range checking of classes added 
            post => sub {
                my ($self) = shift;
                my $type = @{$self->hasReference}[-1];
                return unless defined $type and $type->type;
                my $range = new ontology::dumontierlab::com::hasReference->range();
                return unless $range;
                $range = $self->uri2package($range);
                eval {
                    $range = $range->new();
                };
                return if $@;
                $self->throw("\n" . $type->type() . "\nis not related to\n" . $range->type()) unless $type->isa(ref($range));
            },
            is_array => 1, },
    
    );

...

So our class now has the additional method hasReference and it accepts arguments of type SADI::Data::OWL::Class. In otherwords, you will have to instantiate classes and pass them as arguments to this method.

# for single values
$class->hasReference(sadiframework::org::examples::example::AnnotatedGeneID_Record->new("http://lsrn.org/Gene:642"));

# for setting multiple values
$class->add_hasReference(sadiframework::org::examples::example::AnnotatedGeneID_Record->new("http://lsrn.org/Gene:642"));
$class->add_hasReference(sadiframework::org::examples::example::AnnotatedGeneID_Record->new("http://lsrn.org/Gene:123"));

When developing SADI services with SADISeS, you will not have to add the location of generated modules because the entry scripts generated for each service already do this for you. For those of you wishing to use these generated modules on their own, you simply need to add a use lib statement to your Perl script.

#for example, 
use lib '/your_home_directory/Perl-SADI/generated';

Putting it all together (a complete example script)

The following illustrates a simple script that utilizes the modules mentioned in this tutorial and outputs RDF/XML describing an instance of a AnnotatedGeneID_Record.

#!/usr/bin/perl -w
use strict;

# add the generated libs to @INC 
# modify this as necessary to point to your generated directory
use lib '/home/ubuntu/Perl-SADI/generated';

# use statements for OWL classes
use sadiframework::org::examples::example::AnnotatedGeneID_Record;
use sadiframework::org::ontologies::predicates::hasName;
use ontology::dumontierlab::com::hasSymbol;
use sadiframework::org::ontologies::predicates::hasDescription;
use sadiframework::org::ontologies::predicates::hasProteinName;

# use statements for serializing OWL class
use RDF::Core::Storage::Memory;
use RDF::Core::Model;
use RDF::Core::Model::Serializer;

# instantiate our OWL class
my $class =
  sadiframework::org::examples::example::AnnotatedGeneID_Record->new(
                                                  'http://lsrn.org/GeneID:642');
# add a label
$class->label("some label");

# add datatype properties
$class->add_hasSymbol(
              new ontology::dumontierlab::com::hasSymbol('some symbol value') );
$class->add_hasDescription(
            new sadiframework::org::ontologies::predicates::hasDescription(
                                                             'some description')
);
$class->add_hasProteinName(
            new sadiframework::org::ontologies::predicates::hasProteinName(
                                                            'some protein name')
);
$class->add_hasName(
                   new sadiframework::org::ontologies::predicates::hasName(
                                                              'some name value')
);

# add another symbol
$class->add_hasSymbol(
           new ontology::dumontierlab::com::hasSymbol('another symbol value') );

# here we serialze the OWL class
use OWL::Utils;
print OWL::Utils::serialize($class)

This produces the following RDF/XML:

<rdf:RDF
  xmlns:a="http://sadiframework.org/ontologies/predicates.owl#"
  xmlns:c="http://www.w3.org/2000/01/rdf-schema#"
  xmlns:b="http://ontology.dumontierlab.com/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about="http://lsrn.org/GeneID:642">
    <rdf:type rdf:resource="http://sadiframework.org/examples/example.owl#AnnotatedGeneID_Record"/>
    <c:label>some label</c:label>
    <b:hasSymbol>some symbol value</b:hasSymbol>
    <b:hasSymbol>another symbol value</b:hasSymbol>
    <a:hasDescription>some description</a:hasDescription>
    <a:hasProteinName>some protein name</a:hasProteinName>
    <a:hasName>some name value</a:hasName>
  </rdf:Description>
</rdf:RDF>