org.obolibrary.obo2owl.OboInOwlCardinalityTools Maven / Gradle / Ivy
The newest version!
package org.obolibrary.obo2owl;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_AUTO_GENERATED_BY;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_COMMENT;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_CREATED_BY;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_CREATION_DATE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_DATE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_DEF;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_DEFAULT_NAMESPACE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_DOMAIN;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_FORMAT_VERSION;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_ANONYMOUS;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_ANTI_SYMMETRIC;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_CYCLIC;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_FUNCTIONAL;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_INVERSE_FUNCTIONAL;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_OBSELETE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_REFLEXIVE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_SYMMETRIC;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_IS_TRANSITIVE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_NAME;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_NAMESPACE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_ONTOLOGY;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_RANGE;
import static org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag.TAG_SAVED_BY;
import static org.semanticweb.owlapi.model.parameters.Imports.INCLUDED;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asUnorderedSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.obolibrary.oboformat.model.Frame;
import org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag;
import org.semanticweb.owlapi.model.AddOntologyAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLRuntimeException;
import org.semanticweb.owlapi.model.RemoveOntologyAnnotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Tools for checking and fixing cardinality constrains for OBO ontologies in OWL.
*/
public final class OboInOwlCardinalityTools {
protected static final Logger LOGGER = LoggerFactory.getLogger(OboInOwlCardinalityTools.class);
/**
* Default handler.
*/
public static final AnnotationCardinalityConfictHandler DEFAULT_HANDLER =
new AnnotationCardinalityConfictHandler() {
@Override
public List handleConflict(OWLEntity entity,
OWLAnnotationProperty property, Collection axioms) {
if (axioms.size() > 1) {
String tag = OWLAPIOwl2Obo.owlObjectToTag(property);
if (tag == null) {
tag = property.getIRI().toString();
}
// take the first one in the collection
// (may be random)
LOGGER.info("Fixing multiple {} tags for entity: {}", tag, entity.getIRI());
return listOfFirst(axioms);
}
throw new AnnotationCardinalityException(
"Could not resolve conflict for property: " + property);
}
@Override
public List handleConflict(OWLAnnotationProperty property,
Collection ontologyAnnotations) {
if (ontologyAnnotations.size() > 1) {
String tag = OWLAPIOwl2Obo.owlObjectToTag(property);
if (tag == null) {
tag = property.getIRI().toString();
}
// take the first one in the collection
// (may be random)
LOGGER.info("Fixing multiple ontolgy annotations with, tag: {}", tag);
return listOfFirst(ontologyAnnotations);
}
throw new AnnotationCardinalityException(
"Could not resolve conflict for property: " + property);
}
};
private OboInOwlCardinalityTools() {}
/**
* Check the annotations for cardinality violations. Try to resolve conflicts with the given
* handler.
*
* @param ontology the target ontology
* @param reporter reporter
* @param handler the conflict handler
* @throws AnnotationCardinalityException throws exception in case a conflict cannot be resolved
* by the handler
* @see Frame#check() for implementation in OBO
*/
public static void checkAnnotationCardinality(OWLOntology ontology,
@Nullable AnnotationCardinalityReporter reporter,
@Nullable AnnotationCardinalityConfictHandler handler) {
OWLOntologyManager manager = ontology.getOWLOntologyManager();
OWLDataFactory factory = manager.getOWLDataFactory();
Set headerProperties =
getProperties(factory, TAG_ONTOLOGY, TAG_FORMAT_VERSION, TAG_DATE,
TAG_DEFAULT_NAMESPACE, TAG_SAVED_BY, TAG_AUTO_GENERATED_BY);
checkOntologyAnnotations(headerProperties, ontology, reporter, handler, manager);
Set properties = getProperties(factory, TAG_IS_ANONYMOUS, TAG_NAME,
TAG_NAMESPACE, TAG_DEF, TAG_COMMENT, TAG_DOMAIN, TAG_RANGE, TAG_IS_ANTI_SYMMETRIC,
TAG_IS_CYCLIC, TAG_IS_REFLEXIVE, TAG_IS_SYMMETRIC, TAG_IS_TRANSITIVE, TAG_IS_FUNCTIONAL,
TAG_IS_INVERSE_FUNCTIONAL, TAG_IS_OBSELETE, TAG_CREATED_BY, TAG_CREATION_DATE);
ontology.classesInSignature(INCLUDED)
.forEach(c -> checkOwlEntity(c, properties, ontology, reporter, handler));
ontology.objectPropertiesInSignature(INCLUDED)
.forEach(p -> checkOwlEntity(p, properties, ontology, reporter, handler));
}
private static Set getProperties(OWLDataFactory factory,
OboFormatTag... tags) {
Set set = new HashSet<>();
for (OboFormatTag tag : tags) {
set.add(factory.getOWLAnnotationProperty(OWLAPIObo2Owl.trTagToIRI(tag.getTag())));
}
return set;
}
private static void checkOntologyAnnotations(Set properties,
OWLOntology ontology, @Nullable AnnotationCardinalityReporter reporter,
@Nullable AnnotationCardinalityConfictHandler handler, OWLOntologyManager manager) {
Set annotations = asUnorderedSet(ontology.annotations());
Map> groupedAnnotations = new HashMap<>();
for (OWLAnnotation annotation : annotations) {
OWLAnnotationProperty current = annotation.getProperty();
if (properties.contains(current)) {
Set set = groupedAnnotations.get(current);
if (set == null) {
groupedAnnotations.put(current, Collections.singleton(annotation));
} else if (set.size() == 1) {
set = new HashSet<>(set);
set.add(annotation);
groupedAnnotations.put(current, set);
} else {
set.add(annotation);
}
}
}
// check cardinality constraint
for (Map.Entry> e : groupedAnnotations
.entrySet()) {
if (e.getValue().size() > 1) {
if (reporter != null) {
// report conflict
reporter.reportConflict(e.getKey(), e.getValue());
}
if (handler != null) {
// handle conflict
// if conflict is not resolvable, throws exception
List changed = handler.handleConflict(e.getKey(), e.getValue());
e.getValue().forEach(
a -> manager.applyChange(new RemoveOntologyAnnotation(ontology, a)));
changed
.forEach(a -> manager.applyChange(new AddOntologyAnnotation(ontology, a)));
}
}
}
}
private static void checkOwlEntity(OWLEntity owlClass, Set properties,
OWLOntology ontology, @Nullable AnnotationCardinalityReporter reporter,
@Nullable AnnotationCardinalityConfictHandler handler) {
Map> groupedAxioms =
new HashMap<>();
for (OWLAnnotationAssertionAxiom axiom : asUnorderedSet(
ontology.annotationAssertionAxioms(owlClass.getIRI()))) {
OWLAnnotationProperty current = axiom.getProperty();
if (properties.contains(current)) {
Set set = groupedAxioms.get(current);
if (set == null) {
groupedAxioms.put(current, Collections.singleton(axiom));
} else if (set.size() == 1) {
set = new HashSet<>(set);
set.add(axiom);
groupedAxioms.put(current, set);
} else {
set.add(axiom);
}
}
}
// check cardinality constraint
for (Map.Entry> e : groupedAxioms
.entrySet()) {
if (e.getValue().size() > 1) {
if (reporter != null) {
// report conflict
reporter.reportConflict(owlClass, e.getKey(), e.getValue());
}
if (handler != null) {
// handle conflict
// if conflict is not resolvable, throws exception
List changed =
handler.handleConflict(owlClass, e.getKey(), e.getValue());
ontology.remove(e.getValue());
ontology.add(changed);
}
}
}
}
/**
* Check the annotations for cardinality violations. Try to resolve conflicts with the default
* handler.
*
* @param ontology the target ontology
* @throws AnnotationCardinalityException throws exception in case a conflict cannot be resolved
* by the handler
* @see #DEFAULT_HANDLER
*/
public static void checkAnnotationCardinality(OWLOntology ontology) {
checkAnnotationCardinality(ontology, null, DEFAULT_HANDLER);
}
/**
* Check the annotations for cardinality violations. Only report violations via the given
* reporter
*
* @param ontology the target ontology
* @param reporter used to report violations
*/
public static void checkAnnotationCardinality(OWLOntology ontology,
AnnotationCardinalityReporter reporter) {
try {
checkAnnotationCardinality(ontology, reporter, null);
} catch (AnnotationCardinalityException e) {
// this will not happen as no handler is registered
LOGGER.error("Cardinality exception during report: This isn't supposed to happen.", e);
}
}
static List listOfFirst(Collection t) {
return Collections.singletonList(t.iterator().next());
}
/**
* Functor for resolving conflicts for an annotation property and its cardinality constraint.
*/
public interface AnnotationCardinalityConfictHandler {
/**
* Resolve a conflict for a given annotation property and axioms. The result is either a
* list of resolved axioms or an exception thrown by this method.
*
* @param entity entity
* @param property property
* @param axioms axioms
* @return list of resolved axioms
*/
List handleConflict(OWLEntity entity,
OWLAnnotationProperty property, Collection axioms);
/**
* Resolve a conflict for a given annotation property and ontology annotations. The result
* is either a list of resolved annotations or an exception thrown by this method.
*
* @param property property
* @param ontologyAnnotations ontology annotations
* @return list of resolved annotations
*/
List handleConflict(OWLAnnotationProperty property,
Collection ontologyAnnotations);
}
/**
* Functor for reporting conflicts for an annotation property and its cardinality constraint.
*/
public interface AnnotationCardinalityReporter {
/**
* Report a conflict for a given annotation property and axioms.
*
* @param entity entity
* @param property property
* @param axioms axioms
*/
void reportConflict(OWLEntity entity, OWLAnnotationProperty property,
Collection axioms);
/**
* Report a conflict for a given annotation property and ontology annotations.
*
* @param property property
* @param ontologyAnnotations ontology annotations
*/
void reportConflict(OWLAnnotationProperty property,
Collection ontologyAnnotations);
}
/**
* Exception indication a non-resolvable conflict for an annotation property and its cardinality
* constraint.
*/
public static class AnnotationCardinalityException extends OWLRuntimeException {
/**
* Create a new Exception.
*
* @param message message
* @param cause cause
*/
public AnnotationCardinalityException(String message, Throwable cause) {
super(message, cause);
}
/**
* Create a new Exception.
*
* @param message message
*/
public AnnotationCardinalityException(String message) {
super(message);
}
}
}