owlcs / owlapi

OWL API main repository
822 stars 315 forks source link

Serialization to Trig and rdf/json should include a named graph. #1002

Closed NicolasRouquette closed 2 years ago

NicolasRouquette commented 3 years ago

In the OWLApi, there is a 1-to-many correspondence between

Among these storers , the org.semanticweb.owlapi.rio.RioStorer handles several RDF formats, including Trig and RDF/JSON where an ontology IRI should also appear as a named graph context for the ontology's contents. To handle this, the RioStorer constructor accept a set of RDF resource contexts:

    public RioStorer(OWLDocumentFormatFactory ontologyFormat, RDFHandler rioHandler,
        Resource... contexts) 

(see https://github.com/owlcs/owlapi/blob/owlapi-parent-5.1.17/rio/src/main/java/org/semanticweb/owlapi/rio/RioStorer.java#L88)

It turns out that this context is currently always empty!

See: https://github.com/owlcs/owlapi/blob/owlapi-parent-5.1.17/rio/src/main/java/org/semanticweb/owlapi/rio/AbstractRioStorerFactory.java#L54

    public OWLStorer createStorer() {
        return new RioStorer(getFormatFactory());
    }

This affects the ontology manager where the storer is always constructed independently of the ontology to save. This means that for formats like trig and rdf/json, there is simply no way using the ontology manager to serialize ontologies with a named graph context.

https://github.com/owlcs/owlapi/blob/owlapi-parent-5.1.17/impl/src/main/java/uk/ac/manchester/cs/owl/owlapi/OWLOntologyManagerImpl.java#L1322

    public void saveOntology(OWLOntology ontology, OWLDocumentFormat ontologyFormat,
        IRI documentIRI) throws OWLOntologyStorageException {
        readLock.lock();
        try {
            for (OWLStorerFactory storerFactory : ontologyStorers) {
                OWLStorer storer = storerFactory.createStorer();
                if (storer.canStoreOntology(ontologyFormat)) {
                    storer.storeOntology(ontology, documentIRI, ontologyFormat);
                    return;
                }
            }

Of course, one could create storers outside of the ontology manager; however, it would be better for the ontology manager to create context-aware storers in the first place. This is doable but it requires changing the protocol for creating an OWLStorer:

public interface OWLStorerFactory extends Serializable, Function<OWLOntology, OWLStorer> {

    /**
     * Create new storer.
     *
     * @return new storer
     */
    OWLStorer createStorer(OWLOntology ontology);

This then allows the ontology manager to create an ontology-specific storer like this:

    public void saveOntology(OWLOntology ontology, OWLDocumentFormat ontologyFormat,
        IRI documentIRI) throws OWLOntologyStorageException {
        readLock.lock();
        try {
            for (OWLStorerFactory storerFactory : ontologyStorers) {
                OWLStorer storer = storerFactory.createStorer(ontology);
                if (storer.canStoreOntology(ontologyFormat)) {
                    storer.storeOntology(ontology, documentIRI, ontologyFormat);
                    return;
                }
            }

This allows context-aware storers like Rio to use the information about the ontology IRI, if available, to set the context:

public abstract class AbstractRioStorerFactory extends OWLStorerFactoryImpl {

    protected AbstractRioStorerFactory(OWLDocumentFormatFactory format) {
        super(format);
    }

    @Override
    public OWLStorer createStorer(OWLOntology ontology) {
        return ontology
                .getOntologyID()
                .getOntologyIRI()
                .map(iri -> new RioStorer(getFormatFactory(), SimpleValueFactory.getInstance().createIRI(iri.toString())))
                .orElse(new RioStorer(getFormatFactory()));
    }
}
ignazio1977 commented 3 years ago

Thanks for the detailed report and the pull request, I'm in the process of reviewing it.