/** * Copyright (c) 2015 Lemur Consulting Ltd. * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.co.flax.biosolr.ontology.core.owl; import org.apache.commons.lang3.StringUtils; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.reasoner.Node; import org.semanticweb.owlapi.reasoner.NodeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.flax.biosolr.ontology.core.AbstractOntologyHelper; import uk.co.flax.biosolr.ontology.core.OntologyHelperConfiguration; import uk.co.flax.biosolr.ontology.core.OntologyHelperException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.*; import java.util.stream.Collectors; /** * OWL-specific implementation of OntologyHelper. * * <p>Created by Matt Pearce on 20/10/15.</p> * @author Matt Pearce */ public class OWLOntologyHelper extends AbstractOntologyHelper { private static final Logger LOGGER = LoggerFactory.getLogger(OWLOntologyHelper.class); private final OWLOntologyConfiguration config; private final OWLDataManager dataManager; private final Map<IRI, Collection<String>> labels = new HashMap<>(); private final Map<IRI, Collection<String>> synonyms = new HashMap<>(); private final Map<IRI, Collection<String>> definitions = new HashMap<>(); private long lastCallTime; /** * Construct a new ontology helper instance with a string representing the * ontology URI. * * @param config the ontology configuration, containing the property URIs * for labels, synonyms, etc. * @throws URISyntaxException if the URI cannot be parsed. */ public OWLOntologyHelper(OWLOntologyConfiguration config) throws URISyntaxException { this(new URI(config.getOntologyUri()), config); } /** * Construct a new ontology helper instance. * * @param ontologyUri the URI giving the location of the ontology. * @param config the ontology configuration, containing the property URIs * for labels, synonyms, etc. * @throws URISyntaxException if the URI cannot be parsed. */ public OWLOntologyHelper(URI ontologyUri, OWLOntologyConfiguration config) throws URISyntaxException { this.config = config; if (!ontologyUri.isAbsolute()) { // Try to read as a file from the resource path LOGGER.debug("Ontology URI {} is not absolute - loading from classpath", ontologyUri); URL fileUrl = this.getClass().getClassLoader().getResource(ontologyUri.toString()); if (fileUrl != null) { ontologyUri = fileUrl.toURI(); } else { throw new URISyntaxException(ontologyUri.toString(), "Could not build URL for file"); } } this.dataManager = new OWLDataManager(ontologyUri); } @Override public void updateLastCallTime() { this.lastCallTime = System.currentTimeMillis(); } @Override public long getLastCallTime() { return lastCallTime; } @Override public void dispose() { dataManager.dispose(); // Empty caches labels.clear(); synonyms.clear(); definitions.clear(); } @Override protected OntologyHelperConfiguration getConfiguration() { return config; } @Override public boolean isIriInOntology(String iri) throws OntologyHelperException { return dataManager.isIriInOntology(IRI.create(iri)); } @Override public Collection<String> findLabels(String iri) throws OntologyHelperException { return findLabels(dataManager.getOntology(), IRI.create(iri)); } @Override public Collection<String> findLabelsForIRIs(Collection<String> iris) throws OntologyHelperException { Set<String> labels = new HashSet<>(); OWLOntology ontology = dataManager.getOntology(); iris.stream() .map(iri -> findLabels(ontology, IRI.create(iri))) .forEach(labels::addAll); return labels; } @Override public Collection<String> findSynonyms(String iri) throws OntologyHelperException { return findSynonyms(IRI.create(iri)); } @Override public Collection<String> findDefinitions(String iri) throws OntologyHelperException { return findDefinitions(IRI.create(iri)); } private Collection<String> findLabels(OWLOntology ontology, IRI iri) { if (!labels.containsKey(iri)) { Collection<String> classNames = findPropertyValueStrings(ontology, config.getLabelPropertyUris(), iri); labels.put(iri, classNames); } return labels.get(iri); } private Collection<String> findSynonyms(IRI iri) throws OntologyHelperException { if (!synonyms.containsKey(iri)) { Collection<String> classNames = findPropertyValueStrings(dataManager.getOntology(), config.getSynonymPropertyUris(), iri); synonyms.put(iri, classNames); } return synonyms.get(iri); } private Collection<String> findDefinitions(IRI iri) throws OntologyHelperException { if (!definitions.containsKey(iri)) { Collection<String> classNames = findPropertyValueStrings(dataManager.getOntology(), config.getDefinitionPropertyUris(), iri); definitions.put(iri, classNames); } return definitions.get(iri); } private Collection<String> findPropertyValueStrings(OWLOntology ontology, List<String> propertyUris, IRI iri) { Collection<String> classNames = new HashSet<>(); OWLDataFactory odf = ontology.getOWLOntologyManager().getOWLDataFactory(); // For every property URI, find the annotations for this entry propertyUris.stream() .map(IRI::create) .map(odf::getOWLAnnotationProperty) .map(prop -> findAnnotationNames(ontology, iri, prop)) .forEach(classNames::addAll); return classNames; } private Collection<String> findAnnotationNames(OWLOntology ontology, IRI iri, OWLAnnotationProperty annotationType) { Collection<String> classNames = new HashSet<>(); // get all literal annotations ontology.getAnnotationAssertionAxioms(iri).forEach(axiom -> { if (axiom.getAnnotation().getProperty().equals(annotationType)) { OWLAnnotationValue value = axiom.getAnnotation().getValue(); Optional<String> name = getOWLAnnotationValueAsString(value); if (name.isPresent()) { classNames.add(name.get()); } } }); return classNames; } private Optional<String> getOWLAnnotationValueAsString(OWLAnnotationValue value) { if (value instanceof IRI) { Optional<String> shortForm = getShortForm((IRI) value); if (shortForm.isPresent()) { return shortForm; } } else if (value instanceof OWLLiteral) { return Optional.of(((OWLLiteral) value).getLiteral()); } return Optional.empty(); } private static Optional<String> getShortForm(IRI entityIRI) { LOGGER.trace("Attempting to extract fragment name of URI '" + entityIRI + "'"); String termURI = entityIRI.toString(); URI entUri = entityIRI.toURI(); // we want the "final part" of the URI... if (!StringUtils.isEmpty(entUri.getFragment())) { // a uri with a non-null fragment, so use this... LOGGER.trace("Extracting fragment name using URI fragment (" + entUri.getFragment() + ")"); return Optional.of(entUri.getFragment()); } else if (entityIRI.toURI().getPath() != null) { // no fragment, but there is a path so try and extract the final // part... if (entityIRI.toURI().getPath().contains("/")) { LOGGER.trace("Extracting fragment name using final part of the path of the URI"); return Optional.of(entityIRI.toURI().getPath() .substring(entityIRI.toURI().getPath().lastIndexOf('/') + 1)); } else { // no final path part, so just return whole path LOGGER.trace("Extracting fragment name using the path of the URI"); return Optional.of(entityIRI.toURI().getPath()); } } else { // no fragment, path is null, we've run out of rules so don't // shorten LOGGER.trace("No rules to shorten this URI could be found (" + termURI + ")"); return Optional.empty(); } } @Override public Collection<String> getChildIris(String iri) throws OntologyHelperException { return getSubclassUris(dataManager.getOWLClass(IRI.create(iri)), true); } @Override public Collection<String> getDescendantIris(String iri) throws OntologyHelperException { return getSubclassUris(dataManager.getOWLClass(IRI.create(iri)), false); } @Override public Collection<String> getParentIris(String iri) throws OntologyHelperException { return getSuperclassUris(dataManager.getOWLClass(IRI.create(iri)), true); } @Override public Collection<String> getAncestorIris(String iri) throws OntologyHelperException { return getSuperclassUris(dataManager.getOWLClass(IRI.create(iri)), false); } private Collection<String> getSubclassUris(OWLClass owlClass, boolean direct) throws OntologyHelperException { if (owlClass == null) { return Collections.emptySet(); } return getUrisFromNodeSet(dataManager.getReasoner().getSubClasses(owlClass, direct)); } private Collection<String> getSuperclassUris(OWLClass owlClass, boolean direct) throws OntologyHelperException { if (owlClass == null) { return Collections.emptySet(); } return getUrisFromNodeSet(dataManager.getReasoner().getSuperClasses(owlClass, direct)); } private Collection<String> getUrisFromNodeSet(NodeSet<OWLClass> nodeSet) { Set<String> uris = new HashSet<>(); for (Node<OWLClass> node : nodeSet) { uris.addAll(extractIris(node)); } return uris; } private Collection<String> extractIris(Node<OWLClass> node) { return node.getEntities().stream() .filter(this::isClassSatisfiable) .map(OWLClass::getIRI) .map(IRI::toString) .collect(Collectors.toSet()); } private boolean isClassSatisfiable(OWLClass owlClass) { return !owlClass.isAnonymous() && !owlClass.getIRI().isNothing(); } @Override public Map<String, Collection<String>> getRelations(String iri) throws OntologyHelperException { Map<String, Collection<String>> restrictions = new HashMap<>(); OWLOntology ontology = dataManager.getOntology(); OWLClass owlClass = dataManager.getOWLClass(IRI.create(iri)); if (owlClass != null) { RestrictionVisitor visitor = new RestrictionVisitor(Collections.singleton(ontology)); for (OWLSubClassOfAxiom ax : ontology.getSubClassAxiomsForSubClass(owlClass)) { OWLClassExpression superCls = ax.getSuperClass(); // Ask our superclass to accept a visit from the RestrictionVisitor // - if it is an existential restriction then our restriction visitor // will answer it - if not our visitor will ignore it superCls.accept(visitor); } for (OWLObjectSomeValuesFrom val : visitor.getSomeValues()) { OWLClassExpression exp = val.getFiller(); // Get the shortname of the property expression String shortForm = null; Set<OWLObjectProperty> signatureProps = val.getProperty().getObjectPropertiesInSignature(); for (OWLObjectProperty sigProp : signatureProps) { Collection<String> labels = findLabels(ontology, sigProp.getIRI()); if (labels.size() > 0) { shortForm = new ArrayList<>(labels).get(0); } } if (shortForm != null && !exp.isAnonymous()) { IRI expIri = exp.asOWLClass().getIRI(); if (!restrictions.containsKey(shortForm)) { restrictions.put(shortForm, new ArrayList<>()); } restrictions.get(shortForm).add(expIri.toString()); } } } return restrictions; } }