--- 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);
+ }
+
+}