diff --git a/robot-command/src/main/java/org/obolibrary/robot/RepairCommand.java b/robot-command/src/main/java/org/obolibrary/robot/RepairCommand.java index 33a38ffc2..77c636afe 100644 --- a/robot-command/src/main/java/org/obolibrary/robot/RepairCommand.java +++ b/robot-command/src/main/java/org/obolibrary/robot/RepairCommand.java @@ -26,6 +26,11 @@ public RepairCommand() { o.addOption("I", "input-iri", true, "load ontology from an IRI"); o.addOption("o", "output", true, "save ontology to a file"); o.addOption("O", "output-iri", true, "set OntologyIRI for output"); + o.addOption( + "m", + "merge-axiom-annotations", + true, + "if true, merge axiom annotations on duplicate axioms"); options = o; } @@ -88,7 +93,7 @@ public void main(String[] args) { * @throws Exception on any problem */ public CommandState execute(CommandState state, String[] args) throws Exception { - OWLOntology outputOntology = null; + OWLOntology outputOntology; CommandLine line = CommandLineHelper.getCommandLine(getUsage(), getOptions(), args); if (line == null) { @@ -104,8 +109,14 @@ public CommandState execute(CommandState state, String[] args) throws Exception outputIRI = inputOntology.getOntologyID().getOntologyIRI().orNull(); } - RepairOperation.repair(inputOntology, ioHelper); + boolean mergeAxiomAnnotations = + CommandLineHelper.getBooleanValue(line, "merge-axiom-annotations", false); + + RepairOperation.repair(inputOntology, ioHelper, mergeAxiomAnnotations); outputOntology = inputOntology; + if (outputIRI != null) { + outputOntology.getOWLOntologyManager().setOntologyDocumentIRI(outputOntology, outputIRI); + } CommandLineHelper.maybeSaveOutput(line, outputOntology); diff --git a/robot-core/src/main/java/org/obolibrary/robot/RepairOperation.java b/robot-core/src/main/java/org/obolibrary/robot/RepairOperation.java index 955300cc3..56ccd1f56 100644 --- a/robot-core/src/main/java/org/obolibrary/robot/RepairOperation.java +++ b/robot-core/src/main/java/org/obolibrary/robot/RepairOperation.java @@ -10,17 +10,7 @@ import org.obolibrary.robot.checks.InvalidReferenceChecker; import org.obolibrary.robot.checks.InvalidReferenceViolation; import org.obolibrary.robot.checks.InvalidReferenceViolation.Category; -import org.semanticweb.owlapi.model.IRI; -import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom; -import org.semanticweb.owlapi.model.OWLAnnotationValue; -import org.semanticweb.owlapi.model.OWLAxiom; -import org.semanticweb.owlapi.model.OWLClass; -import org.semanticweb.owlapi.model.OWLEntity; -import org.semanticweb.owlapi.model.OWLLiteral; -import org.semanticweb.owlapi.model.OWLObjectProperty; -import org.semanticweb.owlapi.model.OWLOntology; -import org.semanticweb.owlapi.model.OWLOntologyChange; -import org.semanticweb.owlapi.model.OWLOntologyManager; +import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.model.parameters.Imports; import org.semanticweb.owlapi.util.OWLEntityRenamer; import org.slf4j.Logger; @@ -37,7 +27,7 @@ public class RepairOperation { * @return a map with default values for all available options */ public static Map getDefaultOptions() { - Map options = new HashMap(); + Map options = new HashMap<>(); // options.put("remove-redundant-subclass-axioms", "true"); return options; @@ -47,23 +37,98 @@ public static Map getDefaultOptions() { * Repairs ontology * * @param ontology the OWLOntology to repair - * @param iohelper IOHelper to work with the ontology + * @param ioHelper IOHelper to work with the ontology */ - public static void repair(OWLOntology ontology, IOHelper iohelper) { - repair(ontology, iohelper, getDefaultOptions()); + public static void repair(OWLOntology ontology, IOHelper ioHelper) { + repair(ontology, ioHelper, getDefaultOptions()); } + /** * Repairs ontology * * @param ontology the OWLOntology to repair - * @param iohelper IOHelper to work with the ontology + * @param ioHelper IOHelper to work with the ontology + * @param mergeAxiomAnnotations if true, merge annotations on duplicate axioms + */ + public static void repair( + OWLOntology ontology, IOHelper ioHelper, boolean mergeAxiomAnnotations) { + repair(ontology, ioHelper, getDefaultOptions(), mergeAxiomAnnotations); + } + + /** + * Repairs ontology + * + * @param ontology the OWLOntology to repair + * @param ioHelper IOHelper to work with the ontology * @param options map of repair options */ - public static void repair(OWLOntology ontology, IOHelper iohelper, Map options) { + public static void repair(OWLOntology ontology, IOHelper ioHelper, Map options) { + repair(ontology, ioHelper, options, false); + } + /** + * Repairs ontology + * + * @param ontology the OWLOntology to reapir + * @param ioHelper IOHelper to work with the ontology + * @param options map of repair options + * @param mergeAxiomAnnotations if true, merge annotations on duplicate axioms + */ + public static void repair( + OWLOntology ontology, + IOHelper ioHelper, + Map options, + boolean mergeAxiomAnnotations) { Set violations = InvalidReferenceChecker.getInvalidReferenceViolations(ontology, true); - repairInvalidReferences(iohelper, ontology, violations); + repairInvalidReferences(ioHelper, ontology, violations); + mergeAxiomAnnotations(ontology); + } + + /** + * Given an ontology, merge the annotations of duplicate axioms to create one axiom with all + * annotations. + * + * @param ontology the OWLOntology to repair + */ + public static void mergeAxiomAnnotations(OWLOntology ontology) { + Map> mergedAxioms = new HashMap<>(); + Set axiomsToMerge = new HashSet<>(); + + // Find duplicated axioms and collect their annotations + // OWLAPI should already merge non-annotated duplicates + for (OWLAxiom axiom : ontology.getAxioms()) { + if (axiom.isAnnotated()) { + axiomsToMerge.add(axiom); + OWLAxiom strippedAxiom = axiom.getAxiomWithoutAnnotations(); + Set annotations = axiom.getAnnotations(); + if (mergedAxioms.containsKey(strippedAxiom)) { + logger.info("Merging annotations on axiom: {}", strippedAxiom.toString()); + Set mergeAnnotations = new HashSet<>(); + mergeAnnotations.addAll(mergedAxioms.get(strippedAxiom)); + mergeAnnotations.addAll(annotations); + mergedAxioms.put(strippedAxiom, mergeAnnotations); + } else { + mergedAxioms.put(strippedAxiom, annotations); + } + } + } + + OWLOntologyManager manager = ontology.getOWLOntologyManager(); + OWLDataFactory dataFactory = manager.getOWLDataFactory(); + // Remove the duplicated axioms + manager.removeAxioms(ontology, axiomsToMerge); + + // Create the axioms with new set of annotations + Set newAxioms = new HashSet<>(); + for (Map.Entry> mergedAxiom : mergedAxioms.entrySet()) { + OWLAxiom axiom = mergedAxiom.getKey(); + if (axiom.isAnnotationAxiom()) { + OWLAxiom newAxiom = axiom.getAnnotatedAxiom(mergedAxiom.getValue()); + newAxioms.add(newAxiom); + } + } + manager.addAxioms(ontology, newAxioms); } /** @@ -132,8 +197,7 @@ public static void repairInvalidReferences( logger.info("PRESERVE: " + axiomsToPreserve); manager.removeAxioms(ontology, axiomsToPreserve); - List changes = new ArrayList(); - changes.addAll(renamer.changeIRI(renameMap)); + List changes = new ArrayList<>(renamer.changeIRI(renameMap)); manager.applyChanges(changes); manager.addAxioms(ontology, axiomsToPreserve); } diff --git a/robot-core/src/test/java/org/obolibrary/robot/RepairOperationTest.java b/robot-core/src/test/java/org/obolibrary/robot/RepairOperationTest.java index 25e50f982..7ae91244a 100644 --- a/robot-core/src/test/java/org/obolibrary/robot/RepairOperationTest.java +++ b/robot-core/src/test/java/org/obolibrary/robot/RepairOperationTest.java @@ -18,7 +18,7 @@ public class RepairOperationTest extends CoreTest { public void testRepair() throws IOException, OWLOntologyCreationException { OWLOntology ontology = loadOntology("/need-of-repair.owl"); IOHelper iohelper = new IOHelper(); - RepairOperation.repair(ontology, iohelper); + RepairOperation.repair(ontology, iohelper, true); iohelper.saveOntology(ontology, "target/foo.owl"); assertIdentical("/repaired.owl", ontology); } diff --git a/robot-core/src/test/resources/need-of-repair.owl b/robot-core/src/test/resources/need-of-repair.owl index 6d5a3f992..f146b3f96 100644 --- a/robot-core/src/test/resources/need-of-repair.owl +++ b/robot-core/src/test/resources/need-of-repair.owl @@ -1,17 +1,33 @@ + xmlns:obo="http://purl.obolibrary.org/obo/"> + + + + + + + + + + + diff --git a/robot-core/src/test/resources/repaired.owl b/robot-core/src/test/resources/repaired.owl index b56a49c0f..165b09ddd 100644 --- a/robot-core/src/test/resources/repaired.owl +++ b/robot-core/src/test/resources/repaired.owl @@ -20,6 +20,11 @@ --> + + + + + @@ -38,9 +43,16 @@ + Something duplicated leaf node - + + + + Something duplicated + Annotation 1 + Annotation 2 + @@ -51,6 +63,7 @@ original parent true + @@ -59,11 +72,9 @@ replacement parent - - - +