Switch to side-by-side view

--- a
+++ b/src/main/java/org/sbaresearch/owl/OwlApiFacade.java
@@ -0,0 +1,416 @@
+/**
+ * Copyright (c) 2013/2014 Verein zur Foerderung der IT-Sicherheit in Oesterreich (SBA).
+ * The work has been developed in the TIMBUS Project and the above-mentioned are Members of the TIMBUS Consortium.
+ * TIMBUS is supported by the European Union under the 7th Framework Programme for research and technological
+ * development and demonstration activities (FP7/2007-2013) under grant agreement no. 269940.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including without
+ * limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTIBITLY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise,
+ * unless required by applicable law or agreed to in writing, shall any Contributor be liable for damages, including
+ * any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this
+ * License or out of the use or inability to use the Work.
+ * See the License for the specific language governing permissions and limitation under the License.
+ */
+package org.sbaresearch.owl;
+
+import ch.lambdaj.function.convert.Converter;
+import com.hp.hpl.jena.ontology.OntDocumentManager;
+import com.hp.hpl.jena.ontology.OntModel;
+import com.hp.hpl.jena.ontology.OntModelSpec;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import net.sf.oval.guard.Guarded;
+import net.sf.oval.guard.Pre;
+import org.semanticweb.owlapi.apibinding.OWLManager;
+import org.semanticweb.owlapi.io.FileDocumentTarget;
+import org.semanticweb.owlapi.io.IRIDocumentSource;
+import org.semanticweb.owlapi.io.RDFXMLOntologyFormat;
+import org.semanticweb.owlapi.model.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl;
+
+import java.io.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static ch.lambdaj.Lambda.convert;
+import static ch.lambdaj.Lambda.filter;
+import static org.hamcrest.CoreMatchers.notNullValue;
+
+/**
+ * This class wraps commonly used functions of the API to provide a simpler interface.
+ *
+ * @see <a href="http://owlapi.sourceforge.net/">The OWL API</a>
+ */
+@Guarded
+public class OwlApiFacade {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OwlApiFacade.class);
+
+    private OWLOntologyManager manager;
+    private OWLOntology ontology;
+    private OWLDataFactory dataFactory;
+
+    /**
+     * Wraps an existing ontology.
+     *
+     * @param ontology An existing ontology.
+     */
+    public OwlApiFacade(OWLOntology ontology) {
+        this.ontology = ontology;
+        this.manager = ontology.getOWLOntologyManager();
+        this.dataFactory = manager.getOWLDataFactory();
+    }
+
+    /**
+     * Use OWLManager.createOWLOntologyManager() and OWLManager.getOWLDataFactory()) per default.
+     *
+     * @param manager     @see org.semanticweb.owlapi.model.OWLOntologyManager
+     * @param dataFactory @see org.semanticweb.owlapi.model.OWLDataFactory
+     */
+    public OwlApiFacade(OWLOntologyManager manager, OWLDataFactory dataFactory) {
+        this.manager = manager;
+        this.dataFactory = dataFactory;
+    }
+
+    @SuppressWarnings("unused")
+    public OWLOntology getOntology() {
+        return ontology;
+    }
+
+    @SuppressWarnings("unused")
+    public OWLDataFactory getDataFactory() {
+        return dataFactory;
+    }
+
+    @SuppressWarnings("unused")
+    public OWLOntologyManager getManager() {
+        return manager;
+    }
+
+    @Pre(expr = "_this.manager!=null", lang = "js")
+    public void setIRIMapping(final Map<String, String> iriMapping) {
+        manager.addIRIMapper(new OWLOntologyIRIMapper() {
+            @Override
+            public IRI getDocumentIRI(IRI iri) {
+                if (!iriMapping.containsKey(iri.toString())) return iri;
+                return IRI.create(iriMapping.get(iri.toString()));
+            }
+        });
+    }
+
+    /**
+     * Load ontology from an URI. Does not throw exceptions if resolving any import fails.
+     *
+     * @param owlUri The ontology to load. This can be either an URL ("http://*") or local path ("file:*").
+     */
+    @Pre(expr = "_this.manager!=null", lang = "js")
+    public void load(String owlUri) throws OWLOntologyCreationException {
+        ontology = manager.loadOntologyFromOntologyDocument(
+                new IRIDocumentSource(IRI.create(owlUri)),
+                new OWLOntologyLoaderConfiguration().setMissingImportHandlingStrategy(MissingImportHandlingStrategy.SILENT));
+        if (ontology.getOntologyID().getOntologyIRI() == null) {
+            LOG.info("Ontology IRI not found, using url...");
+            // manager.setOntologyDocumentIRI(ontology, IRI.create(owlUri)); // FIXME: does not seem to work @#%!$%
+            // FIXME: megahack to force using a valid ontology IRI...
+            manager = OWLManager.createOWLOntologyManager();
+            ontology = manager.createOntology(IRI.create(owlUri), new HashSet<OWLOntology>() {{
+                add(ontology);
+            }});
+        }
+    }
+
+    /**
+     * Load ontology from an URI, resolves missing imports by looking up the alternative locations mapping.
+     * This is useful for unpublished ontologies.
+     *
+     * @param owlUri     The ontology to load. This can be either an URL ("http://*") or local path ("file:*").
+     * @param iriMapping The mapping of import to actual ontology locations.
+     * @throws OWLOntologyCreationException
+     * @see OwlApiFacade::load
+     */
+    @Pre(expr = "_this.manager!=null", lang = "js")
+    public void load(String owlUri, final Map<String, String> iriMapping) throws OWLOntologyCreationException {
+        setIRIMapping(iriMapping);
+        load(owlUri);
+    }
+
+    /**
+     * Loads an ontology from a stream. Does not resolve missing imports but throws an exception.
+     *
+     * @param owlContent The ontology to load.
+     * @throws OWLOntologyCreationException
+     */
+    @Pre(expr = "_this.manager!=null", lang = "js")
+    public void load(InputStream owlContent) throws OWLOntologyCreationException {
+        ontology = manager.loadOntologyFromOntologyDocument(owlContent);
+    }
+
+    /**
+     * Creates an empty ontology. This is useful for testing purposes.
+     *
+     * @param iri The IRI of the new ontology.
+     * @throws OWLOntologyCreationException
+     */
+    @Pre(expr = "_this.manager!=null", lang = "js")
+    public void create(String iri) throws OWLOntologyCreationException {
+        ontology = manager.createOntology(IRI.create(iri));
+    }
+
+    /**
+     * No white spaces are allowed as individual name!
+     */
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public OWLNamedIndividual addIndividual(String name) {
+        OWLNamedIndividual indiv = dataFactory.getOWLNamedIndividual(createIri(name));
+        OWLDeclarationAxiom indivDeclaration = dataFactory.getOWLDeclarationAxiom(indiv);
+        manager.applyChange(new AddAxiom(ontology, indivDeclaration));
+        return indiv;
+    }
+
+    /**
+     * @see OwlApiFacade::addIndividual, owlApiFacade::makeInstanceOf
+     */
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public OWLNamedIndividual addIndividual(String indivName, String className) {
+        OWLNamedIndividual owlNamedIndividual = addIndividual(indivName);
+        makeInstanceOf(owlNamedIndividual, className);
+        return owlNamedIndividual;
+    }
+
+    /**
+     * Checks if an individual exists. If provided with the fragment only searches in all imports also.
+     *
+     * @param indivName The individual to search for, given by fragment or namespace and fragment.
+     * @return True if found (possibly in an imported ontology), false else.
+     */
+    @Pre(expr = "_this.ontology!=null", lang = "js")
+    public boolean containsIndividual(String indivName) {
+        if (ontology.containsIndividualInSignature(createIri(indivName), true)) return true;
+        if (createIri(indivName).toString().equals(indivName)) return false;
+        for (OWLOntology importedOntology : ontology.getImports()) {
+            String qualifiedIndivName = importedOntology.getOntologyID().getOntologyIRI() + "#" + indivName;
+            if (ontology.containsIndividualInSignature(IRI.create(qualifiedIndivName), true)) return true;
+        }
+        for (OWLImportsDeclaration importDeclaration : ontology.getImportsDeclarations()) {
+            String qualifiedIndivName = importDeclaration.getIRI().toString() + "#" + indivName;
+            if (ontology.containsIndividualInSignature(IRI.create(qualifiedIndivName), true)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks if an individual exists using the label annotation. Also looks in all imports.
+     *
+     * @param label The label to search for.
+     * @return True if an individual with the specified label does exist, false else.
+     */
+    @Pre(expr = "_this.ontology!=null && label!=null", lang = "js")
+    public boolean containsIndividualByLabel(String label) {
+        return containsIndividualByLabel(ontology, label);
+    }
+
+    private boolean containsIndividualByLabel(OWLOntology ontology, String label) {
+        for (OWLNamedIndividual individual : ontology.getIndividualsInSignature(true)) {
+            for (OWLAnnotation annotation : individual.getAnnotations(ontology)) {
+                if (!annotation.getProperty().equals(ontology.getOWLOntologyManager().getOWLDataFactory().getRDFSLabel())) {
+                    continue;
+                }
+                if (((OWLLiteral) annotation.getValue()).getLiteral().equals(label)) {
+                    return true;
+                }
+            }
+        }
+        for (OWLOntology model : ontology.getImports()) {
+            if (containsIndividualByLabel(model, label)) return true;
+        }
+        return false;
+    }
+
+    @Pre(expr = "_this.ontology!=null && _this.dataFactory!=null", lang = "js")
+    public OWLNamedIndividual getIndividual(String indivName) throws OwlElementNotFoundException {
+        IRI indivIri = createIri(indivName);
+        if (!ontology.containsIndividualInSignature(indivIri, true)) throw new OwlElementNotFoundException();
+        return dataFactory.getOWLNamedIndividual(indivIri);
+    }
+
+    /**
+     * This method expects an unqualified name and tries to find it using the base IRI of the ontology but optionally also
+     * using the base IRIs of all imported ontologies.
+     * This is useful to get an individual without knowing the exact ontology it has been defined.
+     *
+     * @param indivName                      The unqualified individual name.
+     * @param searchInImportedOntologiesAlso Specifies whether the search should be extended to all imported ontologies.
+     * @throws OwlElementNotFoundException If no matching individual can be found.
+     */
+    public OWLNamedIndividual getIndividualByUnqualifiedName(String indivName, boolean searchInImportedOntologiesAlso) throws OwlElementNotFoundException {
+        if (containsIndividual(createIri(indivName).toString())) return getIndividual(indivName);
+        if (!searchInImportedOntologiesAlso)
+            throw new OwlElementNotFoundException(String.format("Individual with name %s not found, consider setting searchInImportedOntologiesAlso to true.", indivName));
+        for (OWLOntology importedOntology : ontology.getImports()) {
+            String qualifiedIndivName = importedOntology.getOntologyID().getOntologyIRI() + "#" + indivName;
+            if (containsIndividual(qualifiedIndivName)) return getIndividual(qualifiedIndivName);
+        }
+        for (OWLImportsDeclaration importDeclaration : ontology.getImportsDeclarations()) {
+            String qualifiedIndivName = importDeclaration.getIRI().toString() + "#" + indivName;
+            if (containsIndividual(qualifiedIndivName)) return getIndividual(qualifiedIndivName);
+        }
+        throw new OwlElementNotFoundException();
+    }
+
+    /**
+     * Retrieves an individual by matching label. This method also considers imported ontologies.
+     *
+     * @param label The label to search for (RDFS label).
+     * @return The individual if it could be found.
+     * @throws OwlElementNotFoundException If the individual could not be found.
+     */
+    @Pre(expr = "_this.ontology!=null && label!=null", lang = "js")
+    public OWLNamedIndividual getIndividualByLabel(String label) throws OwlElementNotFoundException {
+        if (!containsIndividualByLabel(label)) {
+            throw new OwlElementNotFoundException(String.format("Individual with label %s not found, consider setting searchInImportedOntologiesAlso to true.", label));
+        }
+        OWLNamedIndividual individual = getIndividualByLabel(ontology, label);
+        if (individual == null) {
+            throw new OwlElementNotFoundException(String.format("Individual with label %s not found, consider setting searchInImportedOntologiesAlso to true.", label));
+        }
+        return individual;
+    }
+
+    private OWLNamedIndividual getIndividualByLabel(OWLOntology ontology, String label) {
+        for (OWLNamedIndividual individual : ontology.getIndividualsInSignature(true)) {
+            for (OWLAnnotation annotation : individual.getAnnotations(ontology)) {
+                if (!annotation.getProperty().equals(ontology.getOWLOntologyManager().getOWLDataFactory().getRDFSLabel())) {
+                    continue;
+                }
+                if (((OWLLiteral) annotation.getValue()).getLiteral().equals(label)) {
+                    return individual;
+                }
+            }
+        }
+        for (OWLOntology model : ontology.getImports()) {
+            OWLNamedIndividual result = getIndividualByLabel(model, label);
+            if (result != null) return result;
+        }
+        return null;
+    }
+
+    /**
+     * Note: This method can be used for individuals only.
+     */
+    @Pre(expr = "_this.ontology!=null && name!=null && name!=''", lang = "js")
+    public IRI createIri(String name) {
+        if (name.contains("#")) {
+            return IRI.create(name); // OwlAPI does not recognize numeric-only fragments, so just split manually
+        }
+        return IRI.create(ontology.getOntologyID().getOntologyIRI() + "#", name);
+    }
+
+    /**
+     * IRI.create does not seem to split numeric fragments correctly, this method tries to.
+     */
+    public static String getFragment(IRI iri) {
+        // does not work for i.e. "http://timbus.teco.edu/ontologies/Scenarios/MusicClassification.owl#7cb4eeb2", split incorrectly by IRI.create
+        // if (iri.getFragment() != null) return iri.getFragment();
+        int prefixEndIndex = iri.toString().lastIndexOf('#') + 1;
+        return iri.toString().substring(prefixEndIndex);
+    }
+
+    /**
+     * IRI.create does not seem to split numeric fragments correctly, this method tries to.
+     */
+    public static String getNamespace(IRI iri) {
+        int prefixEndIndex = iri.toString().lastIndexOf('#') + 1;
+        return iri.toString().substring(0, prefixEndIndex - 1) + "#";
+    }
+
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public void makeInstanceOf(OWLNamedIndividual individual, String baseClass) {
+        OWLClass classExpression = dataFactory.getOWLClass(createIri(baseClass));
+        OWLClassAssertionAxiom classAssertion = dataFactory.getOWLClassAssertionAxiom(classExpression, individual);
+        manager.applyChange(new AddAxiom(ontology, classAssertion));
+    }
+
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public void addObjectProperty(OWLNamedIndividual individual1, String propertyName, OWLNamedIndividual individual2) {
+        OWLObjectProperty objectProperty = dataFactory.getOWLObjectProperty(createIri(propertyName));
+        OWLObjectPropertyAssertionAxiom objectPropertyAssertionAxiom = dataFactory.getOWLObjectPropertyAssertionAxiom(objectProperty, individual1, individual2);
+        manager.applyChange(new AddAxiom(ontology, objectPropertyAssertionAxiom));
+    }
+
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public void addDataProperty(OWLNamedIndividual individual, String propertyName, OWLLiteral owlLiteral) {
+        OWLDataProperty dataProperty = dataFactory.getOWLDataProperty(createIri(propertyName));
+        OWLDataPropertyAssertionAxiom dataPropertyAssertion = dataFactory
+                .getOWLDataPropertyAssertionAxiom(dataProperty, individual, owlLiteral);
+        manager.applyChange(new AddAxiom(ontology, dataPropertyAssertion));
+    }
+
+    @Pre(expr = "_this.dataFactory!=null", lang = "js")
+    public OWLLiteral getOWLLiteral(String data, OWLDatatype datatype) {
+        return dataFactory.getOWLLiteral(data, datatype);
+    }
+
+    @Pre(expr = "_this.ontology!=null && _this.manager!=null", lang = "js")
+    public void save(String filename) throws OWLOntologyStorageException {
+        manager.saveOntology(ontology, new RDFXMLOntologyFormat(), new FileDocumentTarget(new File(filename)));
+    }
+
+    @Pre(expr = "_this.ontology!=null &&  _this.manager!=null && _this.dataFactory!=null", lang = "js")
+    public void addOntology(String owlUri) {
+        OWLImportsDeclaration importsDeclaration = dataFactory.getOWLImportsDeclaration(IRI.create(owlUri));
+        manager.applyChange(new AddImport(ontology, importsDeclaration));
+    }
+
+    @Pre(expr = "_this.ontology!=null &&  _this.manager!=null", lang = "js")
+    public Model getJenaModel(final boolean ignoreMissingImports) throws OWLOntologyStorageException, OWLOntologyCreationException, IOException {
+        // TODO: cache and make more performant
+        OntModel ontologyModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
+        ontologyModel.getDocumentManager().setProcessImports(true);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        manager.saveOntology(ontology, new RDFXMLOntologyFormat(), outputStream);
+        ontologyModel.setDynamicImports(true);
+        ontologyModel.getDocumentManager().setReadFailureHandler(new OntDocumentManager.ReadFailureHandler() {
+            @Override
+            public void handleFailedRead(String url, Model model, Exception e) {
+                LOG.info("Loading imported model: " + url);
+                OwlApiFacade api = new OwlApiFacade(OWLManager.createOWLOntologyManager(), OWLManager.getOWLDataFactory());
+                try {
+                    api.load(url);
+                    model.add(api.getJenaModel(false));
+                } catch (OWLOntologyCreationException | IOException | OWLOntologyStorageException f) {
+                    if (ignoreMissingImports) return;
+                    throw new ModelConversionException(f);
+                }
+            }
+        });
+        ontologyModel.read(new ByteArrayInputStream(outputStream.toByteArray()), ontology.getOntologyID().getOntologyIRI().toString(), "RDF/XML");
+        ontologyModel.loadImports();
+        return ontologyModel;
+    }
+
+    @Pre(expr = "_this.ontology!=null && owlNamedIndividual!=null", lang = "js")
+    public List<IRI> getClassesOfIndividual(OWLNamedIndividual owlNamedIndividual) {
+        Set<OWLClassExpression> types = owlNamedIndividual.getTypes(ontology.getOWLOntologyManager().getOntologies());
+        return filter(notNullValue(), convert(types, new Converter<OWLClassExpression, IRI>() {
+            @Override
+            public IRI convert(OWLClassExpression owlClassExpression) {
+                if (!(owlClassExpression instanceof OWLClassImpl)) return null;
+                return ((OWLClassImpl) owlClassExpression).getIRI();
+            }
+        }));
+    }
+
+    @Pre(expr = "_this.ontology!=null && individual!=null && classIRI!=null", lang = "js")
+    public boolean isIndividualOfClass(OWLNamedIndividual individual, IRI classIRI) {
+        return getClassesOfIndividual(individual).contains(classIRI);
+    }
+
+}