Question

I have a problem when I am trying to modify my OWL file with Jena's OntModel, etc., API. The original OWL file is:

<owl:Class rdf:about="file:///c:/onto_for_code_test/test.owl#DevelopmentProject" /> 
<owl:ObjectProperty rdf:about="file:///c:/onto_for_code_test/test.owl#carriesOut" />
<owl:FunctionalProperty rdf:about="hasProgramme">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty" />
</owl:FunctionalProperty>

I want to change the functional object property to a non-functional object property, but I can't find any APIs about this. The desired OWL file would be:

<owl:Class rdf:about="file:///c:/onto_for_code_test/test.owl#DevelopmentProject" />
<owl:ObjectProperty rdf:about="file:///c:/onto_for_code_test/test.owl#carriesOut" />
<owl:ObjectProperty rdf:about="hasProgramme" />

The following code gets me the OWL that I want:

OntModel otm = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
otm.read(DIRECTORY, null);
ObjectProperty hasProgramme = otm.getProperty(NS + "hasProgramme");
hasProgramme.removeProperty(RDF.type, OWL.FunctionalProperty);` 

However, when I want to convert hasProgramme to a functional property again, using hasProgramme.convertToFunctionalProperty(); does not add hasProgramme rdf:type owl:FunctionalProperty back to the model! As a result, I think that using hasProgramme.removeProperty(RDF.type, OWL.FunctionalProperty); must be incorrect in this case. What is the correct way to convert a functional ObjectProperty to a non-functional property?

Was it helpful?

Solution

Short version

The methods that Jena uses to implement convertToFunctionalProperty (and other polymorphic behavior) uses a lot of caching. By changing the underlying model, you're violating some of the assumptions that the caching makes. It looks like the right way to make the property non functional is to

p.removeProperty( RDF.type, OWL.FunctionalProperty );

as you've done. To make it functional again, add type again:

p.addProperty( RDF.type, OWL.FunctionalProperty );

Long version

Making things not functional

I did a quick search and didn't find anything for turning a functional property into something that's not a functional property. I think that your approach is correct: removing the prop a owl:FunctionalProperty looks like the right way to go about this.

Making things functional

There are a number of methods in the OntProperty interface for turning a property in other kinds of properties, but not for making them not certain kinds of properties. In particular, the convertToFunctional method says that it will:

Answer a facet of this property as a functional property, adding additional information to the model if necessary.

Returns: This property, but converted to a FunctionalProperty facet

However you succeed in making something not a functional property, it looks like convertToFunctional should work for making it functional again. Yet, you said that

However when I want to convert hasProgramme to functional again, hasProgramme.convertToFunctionalProperty(); does not work.

You didn't really provide enough information about what "does not work" means there, but here's an example that highlights, I think, what you meant. Surprisingly, the first call to convertToFunctionalProperty adds the hasProgram rdf:type owl:FunctionalProperty triple, but if you manually remove it, a second call doesn't add it back again!

import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;

import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;

public class FunctionalPropertyExample {
    public static void main(String[] args) {
        String ns = "http://stackoverflow.com/q/23507335/1281433/";
        OntModel model = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM );

        ObjectProperty opHasProgram = model.createOntProperty( ns+"hasProgram" ).convertToObjectProperty();
        System.out.println( "<!-- Model with plain ObjectProperty-->" );
        RDFDataMgr.write( System.out, model, Lang.RDFXML );

        opHasProgram.convertToFunctionalProperty();
        System.out.println( "\n<!-- Model with FunctionalProperty-->" );
        RDFDataMgr.write( System.out, model, Lang.RDFXML );

        opHasProgram.removeProperty( RDF.type, OWL.FunctionalProperty );
        System.out.println( "\n<!-- Model with ObjectProperty again-->" );
        RDFDataMgr.write( System.out, model, Lang.RDFXML );

        opHasProgram.convertToFunctionalProperty();
        System.out.println( "\n<!-- Model with FunctionalProperty again -->" );
        RDFDataMgr.write( System.out, model, Lang.RDFXML );
    }
}
<!-- Model with plain ObjectProperty-->
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
  <owl:ObjectProperty rdf:about="http://stackoverflow.com/q/23507335/1281433/hasProgram">
    <rdf:type rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </owl:ObjectProperty>
</rdf:RDF>

<!-- Model with FunctionalProperty-->
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
  <owl:FunctionalProperty rdf:about="http://stackoverflow.com/q/23507335/1281433/hasProgram">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
    <rdf:type rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </owl:FunctionalProperty>
</rdf:RDF>

<!-- Model with ObjectProperty again-->
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
  <owl:ObjectProperty rdf:about="http://stackoverflow.com/q/23507335/1281433/hasProgram">
    <rdf:type rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </owl:ObjectProperty>
</rdf:RDF>

<!-- Model with FunctionalProperty again -->
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
  <owl:ObjectProperty rdf:about="http://stackoverflow.com/q/23507335/1281433/hasProgram">
    <!-- *** there should be an rdf:type owl:FunctionalProperty assertion here! *** -->
    <rdf:type rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#Property"/>
  </owl:ObjectProperty>
</rdf:RDF>

This is rather bizarre. To figure out what's happening, we need to take a look at the implementation, but the answer, I think, comes down to caching. OntPropertyImpl implements convertToFunctionalProperty with (and we'll follow the chain):

public FunctionalProperty convertToFunctionalProperty() {
    return convertToType( getProfile().FUNCTIONAL_PROPERTY(), "FUNCTIONAL_PROPERTY", FunctionalProperty.class );
}

Then, in OntResourceImpl:

protected <T extends RDFNode> T convertToType( Resource type, String name, Class<T> cls ) {
    checkProfile( type, name );
    if (canAs( cls )) {
        // don't need to update the model, we already can do the given facet
        return as( cls );
    }

    // we're told that adding this rdf:type will make the as() possible - let's see
    addProperty( RDF.type, type );
    return as( cls );
}

So we see that the property would be added if canAs( FunctionalProperty.class ) were returning false. So for some reason, it's still returning true, even after we've removed the triple that indicates that the property is a FunctionalProperty. In EnhNode:

public <X extends RDFNode> boolean canAs( Class<X> t )
    { return canSupport( t ); }

// ...

protected <X extends RDFNode> boolean canSupport( Class<X> t )
{
    if (alreadyHasView( t )) return true;
    if (getGraph() == null) return false;
    Implementation imp = getPersonality().getImplementation( t );
    return imp == null ? false : imp.canWrap( asNode(), getGraph() );
}

Now, we can't be sure, but alreadyHasView( t ) looks suspect here. After all, it did already have a FunctionalProperty view. Now, in Polymorphic:

protected <X extends T> boolean alreadyHasView( Class<X> t )
    { return findExistingView( t ) != null; }

/**
    find an existing view in the ring which is an instance of _t_ and
    return it; otherwise return null. If _this_ is an instance, the
    search takes care to find it first.
*/
private <X extends T> X findExistingView( Class<X> t )
    {
    Polymorphic<T> r = this;
    for (;;)
        {
        if (t.isInstance( r ) && r.isValid()) return t.cast( r );
        r = r.ring;
        if (r == this) return null;
        }
    }

We don't have enough context to know why findExistingView is returning non-null. For that we need to look at addView, which added it in the first place. The implementation isn't really the important part though; the problem is that these views are getting cached and canAs isn't reconsulting to the model in order to see whether the necessary data is still present in the model.

Most of the time, this probably makes sense. After all, if you were able to convert something in the past, you could still have references to those objects. If you modify the underlying model, what do those objects represent now? What should happen if you try to use them? Some of the data that they depend on to function correctly may no longer be in the data. E.g., if you have an OntClass and remove the c rdf:type owl:Class triple, what should happen when you try to use c? It'd be too expensive to do a sanity check at each use. This could be especially bad if some of the necessary data had been inferred from some other source.

I think that the resolution here is that if you want to modify the model, then using the higher level OntModel API may be dangerous, because much of it depends on caching that you're going to be invalidating. If that's the case, I think that making a property non-functional should still be done by removing the triple prop rdf:type owl:FunctionalProperty. It's just that rather than using convertToFunctionalProperty, you should just add the triple prop rdf:type owl:FunctionalProperty again.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top