/**
* 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.constraint.NotNull;
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.semanticweb.owlapi.vocab.OWLRDFVocabulary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl;
import java.io.*;
import java.util.*;
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;
private OWLOntologyIRIMapper iriMapper;
/**
* 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;
}
public OWLOntologyIRIMapper getIriMapper() {
return iriMapper;
}
@Pre(expr = "_this.manager!=null", lang = "js")
public void setIRIMapping(final Map<String, String> iriMapping) {
iriMapper = new OWLOntologyIRIMapper() {
@Override
public IRI getDocumentIRI(IRI iri) {
if (!iriMapping.containsKey(iri.toString())) return iri;
return IRI.create(iriMapping.get(iri.toString()));
}
};
manager.addIRIMapper(iriMapper);
}
/**
* 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));
}
/**
* Adds an individual to the ontology.
* No white spaces are allowed as individual name!
* Returns the new created individual.
*/
@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;
}
/**
* Adds an individual to the ontology that is instance of a specified class.
* @see #addIndividual
* @see #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;
}
/**
* Adds an individual to the ontology with its name generated using the provided label.
* @see #addIndividual(String, String)
*/
public OWLNamedIndividual addIndividualByLabel(String label, String className) {
String indivName = UUID.randomUUID().toString();
OWLNamedIndividual indiv = addIndividual(indivName, className);
addAxiom(label, indiv);
return indiv;
}
/**
* Adds an individual to the ontology using the provided label and name.
* @see #addIndividual(String, String)
*/
public OWLNamedIndividual addIndividualByLabel(String label, String indivName, String className) {
OWLNamedIndividual indiv = addIndividual(indivName, className);
addAxiom(label, indiv);
return indiv;
}
private void addAxiom(String label, OWLNamedIndividual indiv) {
OWLLiteral literal = dataFactory.getOWLLiteral(label);
OWLAnnotation annotation = dataFactory.getOWLAnnotation(
dataFactory.getOWLAnnotationProperty(OWLRDFVocabulary.RDFS_LABEL.getIRI()),
literal);
OWLAxiom axiom = dataFactory.getOWLAnnotationAssertionAxiom(indiv.getIRI(), annotation);
manager.applyChange(new AddAxiom(ontology, axiom));
}
/**
* 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;
}
public IRI findIndividualByFragmentAndLabel(String indivName) {
try {
if (containsIndividual(indivName)) return getIndividualByUnqualifiedName(indivName, true).getIRI();
if (containsIndividualByLabel(indivName)) return getIndividualByLabel(indivName).getIRI();
} catch (OwlElementNotFoundException e) {
// ignore
}
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("#") || 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);
}
public static String toXMLString(@NotNull OWLOntology ontology) throws OWLOntologyStorageException, IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ontology.getOWLOntologyManager().saveOntology(ontology, new RDFXMLOntologyFormat(), outputStream);
String result = outputStream.toString();
outputStream.close();
return result;
}
}