From af67e2f39cc838a9959b35c97ceaf88c6ec4a90a Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Fri, 5 Aug 2022 20:39:13 +0100 Subject: [PATCH 01/17] obo2owl: Treat replaced_by value as an IRI. The value of the `replaced_by` tag in a OBO file should be an ID according to the OBO Flat File Format specification, so we treat it as such. --- .../src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index 8d826d13a6..6320ec06c8 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -1244,6 +1244,11 @@ protected OWLAxiom trGenericClause(@Nonnull OWLAnnotationSubject sub, @Nonnull S } ax = fac.getOWLAnnotationAssertionAxiom(trTagToAnnotationProp(tag), sub, trLiteral(clause.getValue()), annotations); + } else if (tagConstant == OboFormatTag.TAG_REPLACED_BY) { + String curie = (String) clause.getValue(); + IRI iri = oboIdToIRI(curie); + ax = fac.getOWLAnnotationAssertionAxiom(trTagToAnnotationProp(tag), sub, + iri, annotations); } else { // generic // System.out.println("generic clause:"+clause); From 14eb43f85603ad31dfaeb42f7cc5214507e0cea7 Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Fri, 5 Aug 2022 20:42:41 +0100 Subject: [PATCH 02/17] obo2owl: Follow idspace declarations. When the header frame of a OBO file contains `idspace` tags, use them to translate Prefixed-IDs (aka CURIEs) into full IRIs. --- .../main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index 6320ec06c8..b5c0d8c30b 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -548,7 +548,12 @@ public void trHeaderFrame(@Nonnull Frame headerFrame) { trAnnotations(clause)); } } else if (tag == OboFormatTag.TAG_IDSPACE) { - // do not translate, as they are just directives? TODO ask Chris + for (Clause clause : headerFrame.getClauses(t)) { + Object[] values = clause.getValues().toArray(); + String prefix = values[0].toString() + '_'; + String baseurl = values[1].toString(); + idSpaceMap.put(prefix, baseurl); + } } else if (tag == OboFormatTag.TAG_OWL_AXIOMS) { // in theory, there should only be one tag // but we can silently collapse multiple tags From 19fe6f9cf03de3b2e869be30b85b6b39708870dd Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Sat, 6 Aug 2022 14:14:26 +0100 Subject: [PATCH 03/17] obo2owl: Treat `consider` value as IRI. Process `consider` tags in a OBO file the same way as `replaced_by` tags. --- .../src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index b5c0d8c30b..521e75c056 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -1249,7 +1249,8 @@ protected OWLAxiom trGenericClause(@Nonnull OWLAnnotationSubject sub, @Nonnull S } ax = fac.getOWLAnnotationAssertionAxiom(trTagToAnnotationProp(tag), sub, trLiteral(clause.getValue()), annotations); - } else if (tagConstant == OboFormatTag.TAG_REPLACED_BY) { + } else if (tagConstant == OboFormatTag.TAG_REPLACED_BY + || tagConstant == OboFormatTag.TAG_CONSIDER) { String curie = (String) clause.getValue(); IRI iri = oboIdToIRI(curie); ax = fac.getOWLAnnotationAssertionAxiom(trTagToAnnotationProp(tag), sub, From 6129d484f009913b943e8db630ece462370b1120 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Thu, 11 May 2023 13:50:48 -0400 Subject: [PATCH 04/17] Remove injected OBO_REL prefix declaration; order is backwards so likely never useful. --- .../src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index 1400e68e14..b4f50f337d 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -171,8 +171,6 @@ public class OWLAPIOwl2Obo { protected final void init() { idSpaceMap = new HashMap<>(); - // legacy: - idSpaceMap.put("http://www.obofoundry.org/ro/ro.owl#", "OBO_REL"); untranslatableAxioms = new HashSet<>(); apToDeclare = new HashSet<>(); } From 51644b8df72a7b1df5ed47efe6a65cb5c7db9377 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Fri, 12 May 2023 18:07:41 -0400 Subject: [PATCH 05/17] Roundtrip support for prefix declarations in OBO format. --- .../owlapi/formats/OBODocumentFormat.java | 12 +- .../org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 6 +- .../org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 257 ++++++++++-------- .../oboformat/OBOFormatOWLAPIParser.java | 7 +- .../owlapi/oboformat/OBOFormatRenderer.java | 3 + 5 files changed, 164 insertions(+), 121 deletions(-) diff --git a/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java b/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java index fea0f5e863..8ef3b8a380 100644 --- a/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java +++ b/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java @@ -21,7 +21,7 @@ * Informatics Group * @since 2.0.0 */ -public class OBODocumentFormat extends OWLDocumentFormatImpl { +public class OBODocumentFormat extends PrefixDocumentFormatImpl { /** * Key for validation parameter. Currently supports Boolean.TRUE and @@ -36,14 +36,4 @@ public String getKey() { return "OBO Format"; } - @Override - public boolean isPrefixOWLOntologyFormat() { - return false; - } - - @Override - public PrefixDocumentFormat asPrefixOWLOntologyFormat() { - throw new UnsupportedOperationException(getClass().getName() - + " is not a PrefixDocumentFormat"); - } } diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index 521e75c056..58987bd30b 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -253,6 +253,10 @@ public void setObodoc(OBODoc obodoc) { this.obodoc = obodoc; } + public Map getIdSpaceMap() { + return Collections.unmodifiableMap(this.idSpaceMap); + } + /** * Gets the owl ontology. * @@ -550,7 +554,7 @@ public void trHeaderFrame(@Nonnull Frame headerFrame) { } else if (tag == OboFormatTag.TAG_IDSPACE) { for (Clause clause : headerFrame.getClauses(t)) { Object[] values = clause.getValues().toArray(); - String prefix = values[0].toString() + '_'; + String prefix = values[0].toString(); String baseurl = values[1].toString(); idSpaceMap.put(prefix, baseurl); } diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index b4f50f337d..417b0c41f9 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -5,17 +5,7 @@ import java.io.UnsupportedEncodingException; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,57 +22,7 @@ import org.obolibrary.oboformat.model.Xref; import org.obolibrary.oboformat.parser.OBOFormatConstants; import org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag; -import org.semanticweb.owlapi.model.AxiomType; -import org.semanticweb.owlapi.model.IRI; -import org.semanticweb.owlapi.model.OWLAnnotation; -import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom; -import org.semanticweb.owlapi.model.OWLAnnotationProperty; -import org.semanticweb.owlapi.model.OWLAnnotationValue; -import org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom; -import org.semanticweb.owlapi.model.OWLAxiom; -import org.semanticweb.owlapi.model.OWLClass; -import org.semanticweb.owlapi.model.OWLClassAssertionAxiom; -import org.semanticweb.owlapi.model.OWLClassExpression; -import org.semanticweb.owlapi.model.OWLDataFactory; -import org.semanticweb.owlapi.model.OWLDatatype; -import org.semanticweb.owlapi.model.OWLDeclarationAxiom; -import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom; -import org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom; -import org.semanticweb.owlapi.model.OWLEntity; -import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom; -import org.semanticweb.owlapi.model.OWLEquivalentObjectPropertiesAxiom; -import org.semanticweb.owlapi.model.OWLFunctionalObjectPropertyAxiom; -import org.semanticweb.owlapi.model.OWLInverseFunctionalObjectPropertyAxiom; -import org.semanticweb.owlapi.model.OWLInverseObjectPropertiesAxiom; -import org.semanticweb.owlapi.model.OWLLiteral; -import org.semanticweb.owlapi.model.OWLNamedIndividual; -import org.semanticweb.owlapi.model.OWLNamedObject; -import org.semanticweb.owlapi.model.OWLNaryPropertyAxiom; -import org.semanticweb.owlapi.model.OWLObject; -import org.semanticweb.owlapi.model.OWLObjectAllValuesFrom; -import org.semanticweb.owlapi.model.OWLObjectCardinalityRestriction; -import org.semanticweb.owlapi.model.OWLObjectComplementOf; -import org.semanticweb.owlapi.model.OWLObjectExactCardinality; -import org.semanticweb.owlapi.model.OWLObjectIntersectionOf; -import org.semanticweb.owlapi.model.OWLObjectMaxCardinality; -import org.semanticweb.owlapi.model.OWLObjectMinCardinality; -import org.semanticweb.owlapi.model.OWLObjectProperty; -import org.semanticweb.owlapi.model.OWLObjectPropertyDomainAxiom; -import org.semanticweb.owlapi.model.OWLObjectPropertyExpression; -import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom; -import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom; -import org.semanticweb.owlapi.model.OWLObjectUnionOf; -import org.semanticweb.owlapi.model.OWLOntology; -import org.semanticweb.owlapi.model.OWLOntologyManager; -import org.semanticweb.owlapi.model.OWLQuantifiedObjectRestriction; -import org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom; -import org.semanticweb.owlapi.model.OWLRuntimeException; -import org.semanticweb.owlapi.model.OWLSubAnnotationPropertyOfAxiom; -import org.semanticweb.owlapi.model.OWLSubClassOfAxiom; -import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom; -import org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom; -import org.semanticweb.owlapi.model.OWLSymmetricObjectPropertyAxiom; -import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom; +import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.rdf.rdfxml.parser.RDFConstants; import org.semanticweb.owlapi.vocab.Namespaces; import org.semanticweb.owlapi.vocab.OWL2Datatype; @@ -143,6 +83,18 @@ public class OWLAPIOwl2Obo { * The id space map. */ protected Map idSpaceMap; + + /** + * A PrefixManager which can be used to populate the idSpaceMap + */ + private PrefixManager prefixManager; + + /** + * SortedMap derived from inversion of prefixManager map; used + * for compacting IRIs to CURIEs. + */ + private SortedMap sortedNamespacesToPrefixes = new TreeMap<>(); + /** * The annotation property map. */ @@ -171,6 +123,17 @@ public class OWLAPIOwl2Obo { protected final void init() { idSpaceMap = new HashMap<>(); + // preserve prefix mappings loaded from a previous serialization + if (this.prefixManager != null) { + this.prefixManager.getPrefixName2PrefixMap().forEach((prefix, namespace) -> { + String cleanPrefix = prefix; + if (prefix.endsWith(":")) { + cleanPrefix = prefix.substring(0, prefix.length() - 1); + } + // OBO format doesn't support a default namespace (empty prefix) + if (!cleanPrefix.isEmpty()) idSpaceMap.put(cleanPrefix, namespace); + }); + } untranslatableAxioms = new HashSet<>(); apToDeclare = new HashSet<>(); } @@ -274,6 +237,20 @@ public void setObodoc(@Nonnull OBODoc obodoc) { this.obodoc = obodoc; } + public void setPrefixManager(@Nonnull PrefixManager manager) { + this.prefixManager = manager; + Comparator lengthComparator = Comparator.comparing(s -> -s.length()); + TreeMap nsToPrefix = new TreeMap<>(lengthComparator); + manager.getPrefixName2PrefixMap().forEach((prefix, ns) -> { + String cleanPrefix = prefix; + if (prefix.endsWith(":")) { + cleanPrefix = prefix.substring(0, prefix.length() - 1); + } + if (!cleanPrefix.isEmpty()) nsToPrefix.put(ns, cleanPrefix); + }); + this.sortedNamespacesToPrefixes = nsToPrefix; + } + /** * Convert. * @@ -398,7 +375,7 @@ protected void preProcess() { if (v instanceof OWLLiteral) { viewRel = ((OWLLiteral) v).getLiteral(); } else { - viewRel = getIdentifier((IRI) v); + viewRel = getIdentifier((IRI) v, this.sortedNamespacesToPrefixes); } break; } @@ -478,7 +455,7 @@ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, @Nullable S clause = new Clause(tag, value); f.addClause(clause); } - addQualifiers(clause, annotations); + addQualifiers(clause, annotations, this.sortedNamespacesToPrefixes); return true; } @@ -500,7 +477,7 @@ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, String tag, Clause clause = new Clause(tag); clause.addValue(value); f.addClause(clause); - addQualifiers(clause, annotations); + addQualifiers(clause, annotations, this.sortedNamespacesToPrefixes); return true; } @@ -597,7 +574,7 @@ protected void tr(@Nonnull OWLSubPropertyChainOfAxiom ax) { clause.addValue(rel2); } f.addClause(clause); - addQualifiers(clause, unprocessedAnnotations); + addQualifiers(clause, unprocessedAnnotations, this.sortedNamespacesToPrefixes); } /** @@ -795,7 +772,7 @@ protected void tr(@Nonnull OWLSubObjectPropertyOfAxiom ax) { Frame f = getTypedefFrame((OWLObjectProperty) sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); - addQualifiers(clause, ax.getAnnotations()); + addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } else { error(ax, true); } @@ -811,17 +788,17 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { false); return; } - String tagObject = owlObjectToTag(sup); + String tagObject = owlObjectToTag(sup, this.sortedNamespacesToPrefixes); if (OboFormatTag.TAG_SYNONYMTYPEDEF.getTag().equals(tagObject)) { String name = ""; String scope = null; for (OWLAnnotationAssertionAxiom axiom : getOWLOntology() .getAnnotationAssertionAxioms(sub.getIRI())) { - String tg = owlObjectToTag(axiom.getProperty()); + String tg = owlObjectToTag(axiom.getProperty(), this.sortedNamespacesToPrefixes); if (OboFormatTag.TAG_NAME.getTag().equals(tg)) { name = ((OWLLiteral) axiom.getValue()).getLiteral(); } else if (OboFormatTag.TAG_SCOPE.getTag().equals(tg)) { - scope = owlObjectToTag(axiom.getValue()); + scope = owlObjectToTag(axiom.getValue(), this.sortedNamespacesToPrefixes); } } Frame hf = getObodoc().getHeaderFrame(); @@ -831,7 +808,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { if (scope != null) { clause.addValue(scope); } - addQualifiers(clause, ax.getAnnotations()); + addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); if (!hf.getClauses().contains(clause)) { hf.addClause(clause); } else { @@ -842,7 +819,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { String comment = ""; for (OWLAnnotationAssertionAxiom axiom : getOWLOntology() .getAnnotationAssertionAxioms(sub.getIRI())) { - String tg = owlObjectToTag(axiom.getProperty()); + String tg = owlObjectToTag(axiom.getProperty(), this.sortedNamespacesToPrefixes); if (OboFormatTag.TAG_COMMENT.getTag().equals(tg)) { comment = ((OWLLiteral) axiom.getValue()).getLiteral(); break; @@ -857,7 +834,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { } else { LOG.error("duplicate clause: {} in header", clause); } - addQualifiers(clause, ax.getAnnotations()); + addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); return; } if (sub instanceof OWLObjectProperty && sup instanceof OWLObjectProperty) { @@ -868,7 +845,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { Frame f = getTypedefFrame(sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); - addQualifiers(clause, ax.getAnnotations()); + addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } else { error(ax, true); } @@ -899,7 +876,7 @@ protected void tr(@Nonnull OWLAnnotationAssertionAxiom ax, @Nonnull Frame frame) @SuppressWarnings("null") protected boolean tr(OWLAnnotationProperty prop, @Nonnull OWLAnnotationValue annVal, @Nonnull Set qualifiers, @Nonnull Frame frame) { - String tagString = owlObjectToTag(prop); + String tagString = owlObjectToTag(prop, this.sortedNamespacesToPrefixes); OboFormatTag tag = null; if (tagString != null) { tag = OBOFormatConstants.getTag(tagString); @@ -911,8 +888,8 @@ && isMetadataTag(prop)) { if (propId != null) { Clause clause = new Clause(OboFormatTag.TAG_RELATIONSHIP); clause.addValue(propId); - clause.addValue(getIdentifier((IRI) annVal)); - addQualifiers(clause, qualifiers); + clause.addValue(getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes)); + addQualifiers(clause, qualifiers, this.sortedNamespacesToPrefixes); frame.addClause(clause); return true; } @@ -946,7 +923,7 @@ && isMetadataTag(prop)) { Set unprocessedQualifiers = new HashSet<>(qualifiers); if (tag == OboFormatTag.TAG_DEF) { for (OWLAnnotation aan : qualifiers) { - String propId = owlObjectToTag(aan.getProperty()); + String propId = owlObjectToTag(aan.getProperty(), this.sortedNamespacesToPrefixes); if ("xref".equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; @@ -985,7 +962,7 @@ && isMetadataTag(prop)) { String synonymType = null; handleSynonym(qualifiers, synonymType, clause, unprocessedQualifiers); } - addQualifiers(clause, unprocessedQualifiers); + addQualifiers(clause, unprocessedQualifiers, this.sortedNamespacesToPrefixes); // before adding the clause check for redundant clauses boolean redundant = false; for (Clause frameClause : frame.getClauses()) { @@ -1029,7 +1006,7 @@ protected void handleSynonym(@Nonnull Set qualifiers, @Nullable S String type = null; clause.setXrefs(new ArrayList()); for (OWLAnnotation aan : qualifiers) { - String propId = owlObjectToTag(aan.getProperty()); + String propId = owlObjectToTag(aan.getProperty(), this.sortedNamespacesToPrefixes); if (OboFormatTag.TAG_XREF.getTag().equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; @@ -1082,7 +1059,7 @@ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotati // no built-in obo tag for this: use the generic property_value tag Clause clause = new Clause(OboFormatTag.TAG_PROPERTY_VALUE.getTag()); String propId = getIdentifier(prop); - addQualifiers(clause, qualifiers); + addQualifiers(clause, qualifiers, this.sortedNamespacesToPrefixes); if (!propId.equals("shorthand")) { clause.addValue(propId); if (annVal instanceof OWLLiteral) { @@ -1102,7 +1079,7 @@ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotati clause.addValue(dataTypeIri.toString()); } } else if (annVal instanceof IRI) { - clause.addValue(getIdentifier((IRI) annVal)); + clause.addValue(getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes)); } frame.addClause(clause); } @@ -1124,7 +1101,7 @@ protected Object getValue(@Nonnull OWLAnnotationValue annVal, String tag) { OWLLiteral l = (OWLLiteral) annVal; value = l.isBoolean() ? Boolean.valueOf(l.parseBoolean()) : l.getLiteral(); } else if (annVal instanceof IRI) { - value = getIdentifier((IRI) annVal); + value = getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes); } if (OboFormatTag.TAG_EXPAND_EXPRESSION_TO.getTag().equals(tag)) { String s = value.toString(); @@ -1149,8 +1126,19 @@ protected Object getValue(@Nonnull OWLAnnotationValue annVal, String tag) { * @param qualifiers the qualifiers */ protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set qualifiers) { + addQualifiers(c, qualifiers, new TreeMap<>()); + } + + /** + * Adds the qualifiers. + * + * @param c the c + * @param qualifiers the qualifiers + * @param namespaceToPrefixMap map to pass through for IRI compaction + */ + protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set qualifiers, @Nonnull SortedMap namespaceToPrefixMap) { for (OWLAnnotation ann : qualifiers) { - String prop = owlObjectToTag(ann.getProperty()); + String prop = owlObjectToTag(ann.getProperty(), namespaceToPrefixMap); if (prop == null) { prop = ann.getProperty().getIRI().toString(); } @@ -1161,7 +1149,7 @@ protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set entry : this.idSpaceMap.entrySet()) { + Clause idSpaceClause = new Clause(OboFormatTag.TAG_IDSPACE.getTag()); + idSpaceClause.setValues(Arrays.asList(new String[] {entry.getKey(), entry.getValue() })); + f.addClause(idSpaceClause); + } } /** @@ -1309,7 +1302,7 @@ protected void tr(@Nonnull OWLEquivalentClassesAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_EQUIVALENT_TO.getTag()); c.setValue(cls2); f.addClause(c); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } else if (ce2 instanceof OWLObjectUnionOf) { List list2 = ((OWLObjectUnionOf) ce2).getOperandsAsList(); for (OWLClassExpression oce : list2) { @@ -1322,7 +1315,7 @@ protected void tr(@Nonnull OWLEquivalentClassesAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_UNION_OF.getTag()); c.setValue(id); equivalenceAxiomClauses.add(c); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } } else if (ce2 instanceof OWLObjectIntersectionOf) { List list2 = ((OWLObjectIntersectionOf) ce2).getOperandsAsList(); @@ -1424,7 +1417,7 @@ protected void tr(@Nonnull OWLEquivalentClassesAxiom ax) { assert string != null; c.addQualifierValue(new QualifierValue("all_only", string)); } - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } else if (!f.getClauses(OboFormatTag.TAG_INTERSECTION_OF).isEmpty()) { error( "The axiom is not translated (maximimum one IntersectionOf EquivalenceAxiom)", @@ -1479,7 +1472,7 @@ protected void tr(@Nonnull OWLDisjointClassesAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_DISJOINT_FROM.getTag()); c.setValue(cls2); f.addClause(c); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } /** @@ -1500,11 +1493,11 @@ protected void tr(@Nonnull OWLDeclarationAxiom axiom) { boolean isClass = entity.isOWLClass(); boolean isObjectProperty = entity.isOWLObjectProperty(); // check whether the entity is an alt_id - Optional altIdOptional = checkForOboAltId(set); + Optional altIdOptional = checkForOboAltId(set, this.sortedNamespacesToPrefixes); if (altIdOptional.isPresent()) { // the entity will not be translated // instead create the appropriate alt_id in the replaced_by frame - String currentId = getIdentifier(entity.getIRI()); + String currentId = getIdentifier(entity.getIRI(), this.sortedNamespacesToPrefixes); addAltId(altIdOptional.get().replacedBy, currentId, isClass, isObjectProperty); // add unrelated annotations to untranslatableAxioms axioms untranslatableAxioms.addAll(altIdOptional.get().unrelated); @@ -1520,7 +1513,7 @@ protected void tr(@Nonnull OWLDeclarationAxiom axiom) { for (OWLAxiom a : set) { OWLAnnotationAssertionAxiom ax = (OWLAnnotationAssertionAxiom) a; OWLAnnotationProperty prop = ax.getProperty(); - String tag = owlObjectToTag(prop); + String tag = owlObjectToTag(prop, this.sortedNamespacesToPrefixes); if (OboFormatTag.TAG_IS_METADATA_TAG.getTag().equals(tag)) { f = getTypedefFrame(entity); break; @@ -1584,7 +1577,7 @@ private static class OboAltIdCheckResult { */ @Nonnull private static Optional checkForOboAltId( - Set annotations) { + Set annotations, @Nonnull SortedMap namespaceToPrefixMap) { String replacedBy = null; boolean isMerged = false; boolean isDeprecated = false; @@ -1608,7 +1601,7 @@ private static Optional checkForOboAltId( // fallback: also check for an IRI if (value.asIRI().isPresent()) { // translate IRI to OBO style ID - replacedBy = getIdentifier(value.asIRI().get()); + replacedBy = getIdentifier(value.asIRI().get(), namespaceToPrefixMap); } else { unrelatedAxioms.add(axiom); } @@ -1635,7 +1628,7 @@ private static Optional checkForOboAltId( @Nullable public String getIdentifier(OWLObject obj) { try { - return getIdentifierFromObject(obj, getOWLOntology()); + return getIdentifierFromObject(obj, getOWLOntology(), this.sortedNamespacesToPrefixes); } catch (UntranslatableAxiomException e) { error(e.getMessage(), true); } @@ -1723,12 +1716,29 @@ public static String getIdentifierFromObject(@Nonnull OWLObject obj, @Nonnull OW @Nullable public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology ont) throws UntranslatableAxiomException { + return getIdentifierFromObject(obj, ont, new TreeMap<>()); + } + + /** + * Retrieve the identifier for a given {@link OWLObject}. This methods uses also shorthand hints + * to resolve the identifier. Should the translation process encounter an unexpected axiom an + * + * @param obj the {@link OWLObject} to resolve + * @param ont the target ontology + * @param namespaceToPrefixMap map to pass through for IRI compaction + * @return identifier or null + * @throws UntranslatableAxiomException the untranslatable axiom exception + * {@link UntranslatableAxiomException} is thrown. + */ + @Nullable + public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology ont, @Nonnull SortedMap namespaceToPrefixMap) + throws UntranslatableAxiomException{ if (obj instanceof OWLObjectProperty || obj instanceof OWLAnnotationProperty) { OWLEntity entity = (OWLEntity) obj; Set axioms = - ont.getAnnotationAssertionAxioms(entity.getIRI()); + ont.getAnnotationAssertionAxioms(entity.getIRI()); for (OWLAnnotationAssertionAxiom ax : axioms) { - String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont); + String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont, namespaceToPrefixMap); // see BFOROXrefTest // 5.9.3. Special Rules for Relations if (propId.equals("shorthand")) { @@ -1737,16 +1747,16 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology return ((OWLLiteral) value).getLiteral(); } throw new UntranslatableAxiomException( - "Untranslatable axiom, expected literal value, but was: " + value - + " in axiom: " + ax); + "Untranslatable axiom, expected literal value, but was: " + value + + " in axiom: " + ax); } } } if (obj instanceof OWLEntity) { - return getIdentifier(((OWLEntity) obj).getIRI()); + return getIdentifier(((OWLEntity) obj).getIRI(), namespaceToPrefixMap); } if (obj instanceof IRI) { - return getIdentifier((IRI) obj); + return getIdentifier((IRI) obj, namespaceToPrefixMap); } return null; } @@ -1759,10 +1769,29 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology */ @Nullable public static String getIdentifier(@Nullable IRI iriId) { + return getIdentifier(iriId, new TreeMap<>()); + } + + /** + * See table 5.9.2. Translation of identifiers + * + * @param iriId the iri id + * @param namespaceToPrefixMap map to pass through for IRI compaction + * @return obo identifier or null + */ + @Nullable + public static String getIdentifier(@Nullable IRI iriId, @Nonnull SortedMap namespaceToPrefixMap) { if (iriId == null) { return null; } String iri = iriId.toString(); + boolean match = namespaceToPrefixMap.entrySet().stream().anyMatch(e -> iri.startsWith(e.getKey())); + Optional> mappingOpt = namespaceToPrefixMap.entrySet().stream().filter(e -> iri.startsWith(e.getKey())).findFirst(); + if (mappingOpt.isPresent()) { + Map.Entry mapping = mappingOpt.get(); + String localId = iri.substring(mapping.getKey().length()); + return mapping.getValue() + ":" + localId; + } // canonical IRIs // if (iri.startsWith("http://purl.obolibrary.org/obo/")) { // String canonicalId = iri.replace("http://purl.obolibrary.org/obo/", @@ -1814,7 +1843,7 @@ public static String getIdentifier(@Nullable IRI iriId) { } } if (s.length > 2 && !id.contains("#") - && s[s.length - 1].replaceAll("[0-9]", "").isEmpty()) { + && s[s.length - 1].replaceAll("[0-9]", "").isEmpty()) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < s.length; i++) { if (i > 0) { @@ -1839,6 +1868,18 @@ public static String getIdentifier(@Nullable IRI iriId) { */ @Nullable public static String owlObjectToTag(OWLObject obj) { + return owlObjectToTag(obj, new TreeMap<>()); + } + + /** + * Owl object to tag. + * + * @param obj the object + * @param namespaceToPrefixMap map to pass through for IRI compaction + * @return the string + */ + @Nullable + public static String owlObjectToTag(OWLObject obj, @Nonnull SortedMap namespaceToPrefixMap) { IRI iriObj = null; if (obj instanceof OWLNamedObject) { iriObj = ((OWLNamedObject) obj).getIRI(); @@ -1868,7 +1909,7 @@ public static String owlObjectToTag(OWLObject obj) { if (iri.startsWith(prefix)) { tag = iri.substring(prefix.length()); } else { - tag = getIdentifier(iriObj); + tag = getIdentifier(iriObj, namespaceToPrefixMap); } } return tag; @@ -1881,7 +1922,7 @@ public static String owlObjectToTag(OWLObject obj) { * @return the term frame */ protected Frame getTermFrame(@Nonnull OWLClass entity) { - String id = getIdentifier(entity.getIRI()); + String id = getIdentifier(entity.getIRI(), this.sortedNamespacesToPrefixes); return getTermFrame(id); } @@ -2040,7 +2081,7 @@ protected void tr(@Nonnull OWLSubClassOfAxiom ax) { c.setValue(getIdentifier(sup)); c.setQualifierValues(qvs); f.addClause(c); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); } else if (sup instanceof OWLObjectCardinalityRestriction) { // OWLObjectExactCardinality // OWLObjectMinCardinality @@ -2150,7 +2191,7 @@ protected Clause createRelationshipClauseWithRestrictions( c.addValue(getIdentifier(r.getProperty())); c.addValue(fillerId); c.setQualifierValues(qvs); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); return c; } @@ -2178,7 +2219,7 @@ protected Clause createRelationshipClauseWithCardinality( q = MAX_CARDINALITY; } c.addQualifierValue(new QualifierValue(q, Integer.toString(restriction.getCardinality()))); - addQualifiers(c, ax.getAnnotations()); + addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); return c; } diff --git a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java index f09f027319..cea6424f06 100644 --- a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java +++ b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.net.JarURLConnection; import java.net.URL; +import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; @@ -51,6 +52,7 @@ public OWLDocumentFormat parse(@Nonnull OWLOntologyDocumentSource documentSource // XXX configuration is not used OBOFormatParser p = new OBOFormatParser(); OBODoc obodoc = null; + Map idSpaceMap = null; try { Reader reader = null; InputStream is = null; @@ -93,6 +95,7 @@ public OWLDocumentFormat parse(@Nonnull OWLOntologyDocumentSource documentSource // create a translator object and feed it the OBO Document OWLAPIObo2Owl bridge = new OWLAPIObo2Owl(ontology.getOWLOntologyManager()); bridge.convert(obodoc, ontology); + idSpaceMap = bridge.getIdSpaceMap(); } finally { if (is != null) { is.close(); @@ -104,7 +107,9 @@ public OWLDocumentFormat parse(@Nonnull OWLOntologyDocumentSource documentSource } catch (OBOFormatParserException e) { throw new OWLParserException(e); } - return new OBODocumentFormat(); + OBODocumentFormat format = new OBODocumentFormat(); + if (idSpaceMap != null) format.copyPrefixesFrom(idSpaceMap); + return format; } @Nonnull diff --git a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatRenderer.java b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatRenderer.java index 986d5cae7f..2cdc7b8374 100644 --- a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatRenderer.java +++ b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatRenderer.java @@ -52,6 +52,9 @@ public static void render(@Nonnull OWLOntology ontology, @Nonnull Writer writer, OWLDocumentFormat format) throws OWLOntologyStorageException { try { OWLAPIOwl2Obo translator = new OWLAPIOwl2Obo(ontology.getOWLOntologyManager()); + if (format.isPrefixOWLOntologyFormat()) { + translator.setPrefixManager(format.asPrefixOWLOntologyFormat()); + } final OBODoc result = translator.convert(ontology); boolean hasImports = ontology.getImports().isEmpty() == false; NameProvider nameProvider; From 25cc046e2e84ccd9d2e57d10b0fede4c5d8b9e9d Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Fri, 12 May 2023 21:29:18 -0400 Subject: [PATCH 06/17] Add null check. --- .../src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index 417b0c41f9..79ba70bff0 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -1741,7 +1741,7 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont, namespaceToPrefixMap); // see BFOROXrefTest // 5.9.3. Special Rules for Relations - if (propId.equals("shorthand")) { + if (propId != null && propId.equals("shorthand")) { OWLAnnotationValue value = ax.getValue(); if (value instanceof OWLLiteral) { return ((OWLLiteral) value).getLiteral(); From 7aafdf885a4bbf9b3c803d9f84305fbd084976d6 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Tue, 1 Aug 2023 12:27:48 -0400 Subject: [PATCH 07/17] Use prefix manager directly to compact IRIs to CURIEs. --- .../org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 138 ++++++++---------- 1 file changed, 60 insertions(+), 78 deletions(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index 79ba70bff0..2695b0805c 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -24,6 +24,7 @@ import org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.rdf.rdfxml.parser.RDFConstants; +import org.semanticweb.owlapi.util.DefaultPrefixManager; import org.semanticweb.owlapi.vocab.Namespaces; import org.semanticweb.owlapi.vocab.OWL2Datatype; import org.semanticweb.owlapi.vocab.OWLRDFVocabulary; @@ -87,13 +88,7 @@ public class OWLAPIOwl2Obo { /** * A PrefixManager which can be used to populate the idSpaceMap */ - private PrefixManager prefixManager; - - /** - * SortedMap derived from inversion of prefixManager map; used - * for compacting IRIs to CURIEs. - */ - private SortedMap sortedNamespacesToPrefixes = new TreeMap<>(); + private PrefixManager prefixManager = new DefaultPrefixManager(); /** * The annotation property map. @@ -239,16 +234,6 @@ public void setObodoc(@Nonnull OBODoc obodoc) { public void setPrefixManager(@Nonnull PrefixManager manager) { this.prefixManager = manager; - Comparator lengthComparator = Comparator.comparing(s -> -s.length()); - TreeMap nsToPrefix = new TreeMap<>(lengthComparator); - manager.getPrefixName2PrefixMap().forEach((prefix, ns) -> { - String cleanPrefix = prefix; - if (prefix.endsWith(":")) { - cleanPrefix = prefix.substring(0, prefix.length() - 1); - } - if (!cleanPrefix.isEmpty()) nsToPrefix.put(ns, cleanPrefix); - }); - this.sortedNamespacesToPrefixes = nsToPrefix; } /** @@ -375,7 +360,7 @@ protected void preProcess() { if (v instanceof OWLLiteral) { viewRel = ((OWLLiteral) v).getLiteral(); } else { - viewRel = getIdentifier((IRI) v, this.sortedNamespacesToPrefixes); + viewRel = getIdentifier((IRI) v, this.prefixManager); } break; } @@ -455,7 +440,7 @@ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, @Nullable S clause = new Clause(tag, value); f.addClause(clause); } - addQualifiers(clause, annotations, this.sortedNamespacesToPrefixes); + addQualifiers(clause, annotations, this.prefixManager); return true; } @@ -477,7 +462,7 @@ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, String tag, Clause clause = new Clause(tag); clause.addValue(value); f.addClause(clause); - addQualifiers(clause, annotations, this.sortedNamespacesToPrefixes); + addQualifiers(clause, annotations, this.prefixManager); return true; } @@ -574,7 +559,7 @@ protected void tr(@Nonnull OWLSubPropertyChainOfAxiom ax) { clause.addValue(rel2); } f.addClause(clause); - addQualifiers(clause, unprocessedAnnotations, this.sortedNamespacesToPrefixes); + addQualifiers(clause, unprocessedAnnotations, this.prefixManager); } /** @@ -772,7 +757,7 @@ protected void tr(@Nonnull OWLSubObjectPropertyOfAxiom ax) { Frame f = getTypedefFrame((OWLObjectProperty) sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); - addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(clause, ax.getAnnotations(), this.prefixManager); } else { error(ax, true); } @@ -788,17 +773,17 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { false); return; } - String tagObject = owlObjectToTag(sup, this.sortedNamespacesToPrefixes); + String tagObject = owlObjectToTag(sup, this.prefixManager); if (OboFormatTag.TAG_SYNONYMTYPEDEF.getTag().equals(tagObject)) { String name = ""; String scope = null; for (OWLAnnotationAssertionAxiom axiom : getOWLOntology() .getAnnotationAssertionAxioms(sub.getIRI())) { - String tg = owlObjectToTag(axiom.getProperty(), this.sortedNamespacesToPrefixes); + String tg = owlObjectToTag(axiom.getProperty(), this.prefixManager); if (OboFormatTag.TAG_NAME.getTag().equals(tg)) { name = ((OWLLiteral) axiom.getValue()).getLiteral(); } else if (OboFormatTag.TAG_SCOPE.getTag().equals(tg)) { - scope = owlObjectToTag(axiom.getValue(), this.sortedNamespacesToPrefixes); + scope = owlObjectToTag(axiom.getValue(), this.prefixManager); } } Frame hf = getObodoc().getHeaderFrame(); @@ -808,7 +793,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { if (scope != null) { clause.addValue(scope); } - addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(clause, ax.getAnnotations(), this.prefixManager); if (!hf.getClauses().contains(clause)) { hf.addClause(clause); } else { @@ -819,7 +804,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { String comment = ""; for (OWLAnnotationAssertionAxiom axiom : getOWLOntology() .getAnnotationAssertionAxioms(sub.getIRI())) { - String tg = owlObjectToTag(axiom.getProperty(), this.sortedNamespacesToPrefixes); + String tg = owlObjectToTag(axiom.getProperty(), this.prefixManager); if (OboFormatTag.TAG_COMMENT.getTag().equals(tg)) { comment = ((OWLLiteral) axiom.getValue()).getLiteral(); break; @@ -834,7 +819,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { } else { LOG.error("duplicate clause: {} in header", clause); } - addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(clause, ax.getAnnotations(), this.prefixManager); return; } if (sub instanceof OWLObjectProperty && sup instanceof OWLObjectProperty) { @@ -845,7 +830,7 @@ protected void tr(@Nonnull OWLSubAnnotationPropertyOfAxiom ax) { Frame f = getTypedefFrame(sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); - addQualifiers(clause, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(clause, ax.getAnnotations(), this.prefixManager); } else { error(ax, true); } @@ -876,7 +861,7 @@ protected void tr(@Nonnull OWLAnnotationAssertionAxiom ax, @Nonnull Frame frame) @SuppressWarnings("null") protected boolean tr(OWLAnnotationProperty prop, @Nonnull OWLAnnotationValue annVal, @Nonnull Set qualifiers, @Nonnull Frame frame) { - String tagString = owlObjectToTag(prop, this.sortedNamespacesToPrefixes); + String tagString = owlObjectToTag(prop, this.prefixManager); OboFormatTag tag = null; if (tagString != null) { tag = OBOFormatConstants.getTag(tagString); @@ -888,8 +873,8 @@ && isMetadataTag(prop)) { if (propId != null) { Clause clause = new Clause(OboFormatTag.TAG_RELATIONSHIP); clause.addValue(propId); - clause.addValue(getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes)); - addQualifiers(clause, qualifiers, this.sortedNamespacesToPrefixes); + clause.addValue(getIdentifier((IRI) annVal, this.prefixManager)); + addQualifiers(clause, qualifiers, this.prefixManager); frame.addClause(clause); return true; } @@ -923,7 +908,7 @@ && isMetadataTag(prop)) { Set unprocessedQualifiers = new HashSet<>(qualifiers); if (tag == OboFormatTag.TAG_DEF) { for (OWLAnnotation aan : qualifiers) { - String propId = owlObjectToTag(aan.getProperty(), this.sortedNamespacesToPrefixes); + String propId = owlObjectToTag(aan.getProperty(), this.prefixManager); if ("xref".equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; @@ -962,7 +947,7 @@ && isMetadataTag(prop)) { String synonymType = null; handleSynonym(qualifiers, synonymType, clause, unprocessedQualifiers); } - addQualifiers(clause, unprocessedQualifiers, this.sortedNamespacesToPrefixes); + addQualifiers(clause, unprocessedQualifiers, this.prefixManager); // before adding the clause check for redundant clauses boolean redundant = false; for (Clause frameClause : frame.getClauses()) { @@ -1006,7 +991,7 @@ protected void handleSynonym(@Nonnull Set qualifiers, @Nullable S String type = null; clause.setXrefs(new ArrayList()); for (OWLAnnotation aan : qualifiers) { - String propId = owlObjectToTag(aan.getProperty(), this.sortedNamespacesToPrefixes); + String propId = owlObjectToTag(aan.getProperty(), this.prefixManager); if (OboFormatTag.TAG_XREF.getTag().equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; @@ -1059,7 +1044,7 @@ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotati // no built-in obo tag for this: use the generic property_value tag Clause clause = new Clause(OboFormatTag.TAG_PROPERTY_VALUE.getTag()); String propId = getIdentifier(prop); - addQualifiers(clause, qualifiers, this.sortedNamespacesToPrefixes); + addQualifiers(clause, qualifiers, this.prefixManager); if (!propId.equals("shorthand")) { clause.addValue(propId); if (annVal instanceof OWLLiteral) { @@ -1079,7 +1064,7 @@ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotati clause.addValue(dataTypeIri.toString()); } } else if (annVal instanceof IRI) { - clause.addValue(getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes)); + clause.addValue(getIdentifier((IRI) annVal, this.prefixManager)); } frame.addClause(clause); } @@ -1101,7 +1086,7 @@ protected Object getValue(@Nonnull OWLAnnotationValue annVal, String tag) { OWLLiteral l = (OWLLiteral) annVal; value = l.isBoolean() ? Boolean.valueOf(l.parseBoolean()) : l.getLiteral(); } else if (annVal instanceof IRI) { - value = getIdentifier((IRI) annVal, this.sortedNamespacesToPrefixes); + value = getIdentifier((IRI) annVal, this.prefixManager); } if (OboFormatTag.TAG_EXPAND_EXPRESSION_TO.getTag().equals(tag)) { String s = value.toString(); @@ -1126,7 +1111,7 @@ protected Object getValue(@Nonnull OWLAnnotationValue annVal, String tag) { * @param qualifiers the qualifiers */ protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set qualifiers) { - addQualifiers(c, qualifiers, new TreeMap<>()); + addQualifiers(c, qualifiers, new DefaultPrefixManager()); } /** @@ -1134,11 +1119,11 @@ protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set qualifiers, @Nonnull SortedMap namespaceToPrefixMap) { + protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set qualifiers, @Nonnull PrefixManager pm) { for (OWLAnnotation ann : qualifiers) { - String prop = owlObjectToTag(ann.getProperty(), namespaceToPrefixMap); + String prop = owlObjectToTag(ann.getProperty(), pm); if (prop == null) { prop = ann.getProperty().getIRI().toString(); } @@ -1149,7 +1134,7 @@ protected static void addQualifiers(@Nonnull Clause c, @Nonnull Set list2 = ((OWLObjectUnionOf) ce2).getOperandsAsList(); for (OWLClassExpression oce : list2) { @@ -1315,7 +1300,7 @@ protected void tr(@Nonnull OWLEquivalentClassesAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_UNION_OF.getTag()); c.setValue(id); equivalenceAxiomClauses.add(c); - addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(c, ax.getAnnotations(), this.prefixManager); } } else if (ce2 instanceof OWLObjectIntersectionOf) { List list2 = ((OWLObjectIntersectionOf) ce2).getOperandsAsList(); @@ -1417,7 +1402,7 @@ protected void tr(@Nonnull OWLEquivalentClassesAxiom ax) { assert string != null; c.addQualifierValue(new QualifierValue("all_only", string)); } - addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(c, ax.getAnnotations(), this.prefixManager); } else if (!f.getClauses(OboFormatTag.TAG_INTERSECTION_OF).isEmpty()) { error( "The axiom is not translated (maximimum one IntersectionOf EquivalenceAxiom)", @@ -1472,7 +1457,7 @@ protected void tr(@Nonnull OWLDisjointClassesAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_DISJOINT_FROM.getTag()); c.setValue(cls2); f.addClause(c); - addQualifiers(c, ax.getAnnotations(), this.sortedNamespacesToPrefixes); + addQualifiers(c, ax.getAnnotations(), this.prefixManager); } /** @@ -1493,11 +1478,11 @@ protected void tr(@Nonnull OWLDeclarationAxiom axiom) { boolean isClass = entity.isOWLClass(); boolean isObjectProperty = entity.isOWLObjectProperty(); // check whether the entity is an alt_id - Optional altIdOptional = checkForOboAltId(set, this.sortedNamespacesToPrefixes); + Optional altIdOptional = checkForOboAltId(set, this.prefixManager); if (altIdOptional.isPresent()) { // the entity will not be translated // instead create the appropriate alt_id in the replaced_by frame - String currentId = getIdentifier(entity.getIRI(), this.sortedNamespacesToPrefixes); + String currentId = getIdentifier(entity.getIRI(), this.prefixManager); addAltId(altIdOptional.get().replacedBy, currentId, isClass, isObjectProperty); // add unrelated annotations to untranslatableAxioms axioms untranslatableAxioms.addAll(altIdOptional.get().unrelated); @@ -1513,7 +1498,7 @@ protected void tr(@Nonnull OWLDeclarationAxiom axiom) { for (OWLAxiom a : set) { OWLAnnotationAssertionAxiom ax = (OWLAnnotationAssertionAxiom) a; OWLAnnotationProperty prop = ax.getProperty(); - String tag = owlObjectToTag(prop, this.sortedNamespacesToPrefixes); + String tag = owlObjectToTag(prop, this.prefixManager); if (OboFormatTag.TAG_IS_METADATA_TAG.getTag().equals(tag)) { f = getTypedefFrame(entity); break; @@ -1577,7 +1562,7 @@ private static class OboAltIdCheckResult { */ @Nonnull private static Optional checkForOboAltId( - Set annotations, @Nonnull SortedMap namespaceToPrefixMap) { + Set annotations, @Nonnull PrefixManager pm) { String replacedBy = null; boolean isMerged = false; boolean isDeprecated = false; @@ -1601,7 +1586,7 @@ private static Optional checkForOboAltId( // fallback: also check for an IRI if (value.asIRI().isPresent()) { // translate IRI to OBO style ID - replacedBy = getIdentifier(value.asIRI().get(), namespaceToPrefixMap); + replacedBy = getIdentifier(value.asIRI().get(), pm); } else { unrelatedAxioms.add(axiom); } @@ -1628,7 +1613,7 @@ private static Optional checkForOboAltId( @Nullable public String getIdentifier(OWLObject obj) { try { - return getIdentifierFromObject(obj, getOWLOntology(), this.sortedNamespacesToPrefixes); + return getIdentifierFromObject(obj, getOWLOntology(), this.prefixManager); } catch (UntranslatableAxiomException e) { error(e.getMessage(), true); } @@ -1716,7 +1701,7 @@ public static String getIdentifierFromObject(@Nonnull OWLObject obj, @Nonnull OW @Nullable public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology ont) throws UntranslatableAxiomException { - return getIdentifierFromObject(obj, ont, new TreeMap<>()); + return getIdentifierFromObject(obj, ont, new DefaultPrefixManager()); } /** @@ -1725,20 +1710,20 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology * * @param obj the {@link OWLObject} to resolve * @param ont the target ontology - * @param namespaceToPrefixMap map to pass through for IRI compaction + * @param pm PrefixManager to pass through for IRI compaction * @return identifier or null * @throws UntranslatableAxiomException the untranslatable axiom exception * {@link UntranslatableAxiomException} is thrown. */ @Nullable - public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology ont, @Nonnull SortedMap namespaceToPrefixMap) + public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology ont, @Nonnull PrefixManager pm) throws UntranslatableAxiomException{ if (obj instanceof OWLObjectProperty || obj instanceof OWLAnnotationProperty) { OWLEntity entity = (OWLEntity) obj; Set axioms = ont.getAnnotationAssertionAxioms(entity.getIRI()); for (OWLAnnotationAssertionAxiom ax : axioms) { - String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont, namespaceToPrefixMap); + String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont, pm); // see BFOROXrefTest // 5.9.3. Special Rules for Relations if (propId != null && propId.equals("shorthand")) { @@ -1753,10 +1738,10 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology } } if (obj instanceof OWLEntity) { - return getIdentifier(((OWLEntity) obj).getIRI(), namespaceToPrefixMap); + return getIdentifier(((OWLEntity) obj).getIRI(), pm); } if (obj instanceof IRI) { - return getIdentifier((IRI) obj, namespaceToPrefixMap); + return getIdentifier((IRI) obj, pm); } return null; } @@ -1769,34 +1754,31 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology */ @Nullable public static String getIdentifier(@Nullable IRI iriId) { - return getIdentifier(iriId, new TreeMap<>()); + return getIdentifier(iriId, new DefaultPrefixManager()); } /** * See table 5.9.2. Translation of identifiers * * @param iriId the iri id - * @param namespaceToPrefixMap map to pass through for IRI compaction + * @param pm PrefixManager to pass through for IRI compaction * @return obo identifier or null */ @Nullable - public static String getIdentifier(@Nullable IRI iriId, @Nonnull SortedMap namespaceToPrefixMap) { + public static String getIdentifier(@Nullable IRI iriId, @Nonnull PrefixManager pm) { if (iriId == null) { return null; } - String iri = iriId.toString(); - boolean match = namespaceToPrefixMap.entrySet().stream().anyMatch(e -> iri.startsWith(e.getKey())); - Optional> mappingOpt = namespaceToPrefixMap.entrySet().stream().filter(e -> iri.startsWith(e.getKey())).findFirst(); - if (mappingOpt.isPresent()) { - Map.Entry mapping = mappingOpt.get(); - String localId = iri.substring(mapping.getKey().length()); - return mapping.getValue() + ":" + localId; + String curie = pm.getPrefixIRIIgnoreQName(iriId); + if (curie != null && !curie.startsWith(":")) { + return curie; } // canonical IRIs // if (iri.startsWith("http://purl.obolibrary.org/obo/")) { // String canonicalId = iri.replace("http://purl.obolibrary.org/obo/", // ""); // } + String iri = iriId.toString(); int indexSlash = iri.lastIndexOf('/'); String id = null; if (indexSlash > -1) { @@ -1868,18 +1850,18 @@ public static String getIdentifier(@Nullable IRI iriId, @Nonnull SortedMap()); + return owlObjectToTag(obj, new DefaultPrefixManager()); } /** * Owl object to tag. * * @param obj the object - * @param namespaceToPrefixMap map to pass through for IRI compaction + * @param pm PrefixManager to pass through for IRI compaction * @return the string */ @Nullable - public static String owlObjectToTag(OWLObject obj, @Nonnull SortedMap namespaceToPrefixMap) { + public static String owlObjectToTag(OWLObject obj, @Nonnull PrefixManager pm) { IRI iriObj = null; if (obj instanceof OWLNamedObject) { iriObj = ((OWLNamedObject) obj).getIRI(); @@ -1909,7 +1891,7 @@ public static String owlObjectToTag(OWLObject obj, @Nonnull SortedMap Date: Tue, 1 Aug 2023 13:50:46 -0400 Subject: [PATCH 08/17] Fix lookup for idspace prefixes. --- .../java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index 58987bd30b..0a2bcdebb8 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -1719,10 +1719,12 @@ public IRI oboIdToIRI_load(@Nonnull String id, boolean oboInOwlDefault) { } } String[] idParts = id.split(":", 2); + String prefix = ""; String db; String localId; if (idParts.length > 1) { - db = idParts[0]; + prefix = idParts[0]; + db = prefix; localId = idParts[1]; if (localId.contains("_")) { db += "#_";// NonCanonical-Prefixed-ID @@ -1745,8 +1747,8 @@ public IRI oboIdToIRI_load(@Nonnull String id, boolean oboInOwlDefault) { uriPrefix = OIOVOCAB_IRI_PREFIX; } else { uriPrefix = DEFAULT_IRI_PREFIX + db; - if (idSpaceMap.containsKey(db)) { - uriPrefix = idSpaceMap.get(db); + if (idSpaceMap.containsKey(prefix)) { + uriPrefix = idSpaceMap.get(prefix); } } String safeId; @@ -1786,10 +1788,8 @@ protected String translateShorthandIdToExpandedId(@Nonnull String id) { Collection xrefs = tdf.getTagValues(OboFormatTag.TAG_XREF, Xref.class); String matchingExpandedId = null; for (Xref xref : xrefs) { - // System.err.println("ID:"+id+" xref:"+xref); if (xref != null) { String xid = xref.getIdref(); - // System.err.println(" ID:"+id+" xid:"+xid); if (xid.equals(id)) { continue; } @@ -1806,7 +1806,6 @@ protected String translateShorthandIdToExpandedId(@Nonnull String id) { if (matchingExpandedId == null) { return id; } - // System.err.println(" ID:"+id+" matching:"+matchingExpandedId); return matchingExpandedId; } From 1d949e953c4c5a48227b0bebe3ffd257c79b0329 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Tue, 1 Aug 2023 14:45:24 -0400 Subject: [PATCH 09/17] Don't write default prefixes into OBO format. --- .../owlapi/oboformat/OBOFormatOWLAPIParser.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java index cea6424f06..9fd209e67d 100644 --- a/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java +++ b/oboformat/src/main/java/org/semanticweb/owlapi/oboformat/OBOFormatOWLAPIParser.java @@ -34,10 +34,8 @@ import org.semanticweb.owlapi.io.AbstractOWLParser; import org.semanticweb.owlapi.io.OWLOntologyDocumentSource; import org.semanticweb.owlapi.io.OWLParserException; -import org.semanticweb.owlapi.model.OWLDocumentFormat; -import org.semanticweb.owlapi.model.OWLDocumentFormatFactory; -import org.semanticweb.owlapi.model.OWLOntology; -import org.semanticweb.owlapi.model.OWLOntologyLoaderConfiguration; +import org.semanticweb.owlapi.model.*; +import org.semanticweb.owlapi.util.DefaultPrefixManager; /** oboformat parser */ public class OBOFormatOWLAPIParser extends AbstractOWLParser implements Serializable { @@ -108,6 +106,9 @@ public OWLDocumentFormat parse(@Nonnull OWLOntologyDocumentSource documentSource throw new OWLParserException(e); } OBODocumentFormat format = new OBODocumentFormat(); + PrefixManager pm = new DefaultPrefixManager(); + pm.clear(); + format.setPrefixManager(pm); if (idSpaceMap != null) format.copyPrefixesFrom(idSpaceMap); return format; } From 4a75c94893f8b016d900f0ffa4816df97560913c Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Tue, 1 Aug 2023 14:47:35 -0400 Subject: [PATCH 10/17] Add OBO idspaces usage and roundtrip test. --- .../obolibrary/oboformat/PrefixesTest.java | 53 +++++++++ .../obo/iris_for_obsoletes_replacements.obo | 102 ++++++++++++++++++ .../src/test/resources/obo/test_prefixes.obo | 12 +++ 3 files changed, 167 insertions(+) create mode 100644 contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java create mode 100644 contract/src/test/resources/obo/iris_for_obsoletes_replacements.obo create mode 100644 contract/src/test/resources/obo/test_prefixes.obo diff --git a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java new file mode 100644 index 0000000000..5cea48cbee --- /dev/null +++ b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java @@ -0,0 +1,53 @@ +package org.obolibrary.oboformat; + +import org.junit.jupiter.api.Test; +import org.semanticweb.owlapi.api.test.baseclasses.TestBase; +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.model.*; +import org.semanticweb.owlapi.search.EntitySearcher; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class PrefixesTest extends TestBase { + + @Test + void testPrefixesRoundtrip() throws OWLOntologyStorageException { + OWLDataFactory factory = OWLManager.getOWLDataFactory(); + OWLAnnotationProperty termReplacedBy = factory.getOWLAnnotationProperty(IRI.create("http://purl.obolibrary.org/obo/IAO_0100001")); + OWLAnnotationProperty consider = factory.getOWLAnnotationProperty(IRI.create("http://www.geneontology.org/formats/oboInOwl#consider")); + OWLOntology oboOnt = loadOntology("obo/test_prefixes.obo", OWLManager.createOWLOntologyManager()); + assertTrue(oboOnt.containsEntityInSignature(factory.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/FOO_1234")))); + assertTrue(oboOnt.containsEntityInSignature(factory.getOWLClass(IRI.create("http://somewhere.org/MyClass")))); + Map prefixMap = oboOnt.getOWLOntologyManager().getOntologyFormat(oboOnt).asPrefixOWLOntologyFormat().getPrefixName2PrefixMap(); + assertEquals("http://somewhere.org/", prefixMap.get("sw:")); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + oboOnt.getOWLOntologyManager().saveOntology(oboOnt, stream); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); + assertTrue(roundtripOBO.contains("idspace: sw http://somewhere.org/")); + assertTrue(roundtripOBO.contains("[Term]\nid: FOO:1234\nis_a: sw:MyClass")); + + OWLOntology replacementsOnt = loadOntology("obo/iris_for_obsoletes_replacements.obo", OWLManager.createOWLOntologyManager()); + assertTrue(EntitySearcher.getAnnotationAssertionAxioms(factory.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/GO_0000108")), replacementsOnt).stream() + .filter(ax -> ax.getProperty().equals(termReplacedBy)) + .anyMatch(ax -> ax.getValue().asIRI().get().equals(IRI.create("http://purl.obolibrary.org/obo/GO_0000109"))), + "Values for replaced_by should be IRIs rather than strings"); + assertTrue(EntitySearcher.getAnnotationAssertionAxioms(factory.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/GO_0000114")), replacementsOnt).stream() + .filter(ax -> ax.getProperty().equals(consider)) + .anyMatch(ax -> ax.getValue().asIRI().get().equals(IRI.create("http://purl.obolibrary.org/obo/GO_0000083"))), + "Values for consider should be IRIs rather than strings"); + assertTrue(EntitySearcher.getAnnotationAssertionAxioms(factory.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/GO_0010553")), replacementsOnt).stream() + .filter(ax -> ax.getProperty().equals(termReplacedBy)) + .anyMatch(ax -> ax.getValue().asIRI().get().equals(IRI.create("http://purl.obolibrary.org/obo/GO_0000122"))), + "Values for replaced_by on alt_ids should be IRIs"); + ByteArrayOutputStream stream2 = new ByteArrayOutputStream(); + replacementsOnt.getOWLOntologyManager().saveOntology(replacementsOnt, stream2); + String roundtripReplacementsOnt = new String(stream2.toByteArray(), StandardCharsets.UTF_8); + assertFalse(roundtripReplacementsOnt.contains("idspace:")); + assertTrue(roundtripReplacementsOnt.contains("replaced_by: GO:0000109")); + } + +} diff --git a/contract/src/test/resources/obo/iris_for_obsoletes_replacements.obo b/contract/src/test/resources/obo/iris_for_obsoletes_replacements.obo new file mode 100644 index 0000000000..12945df263 --- /dev/null +++ b/contract/src/test/resources/obo/iris_for_obsoletes_replacements.obo @@ -0,0 +1,102 @@ +format-version: 1.2 +subsetdef: chebi_ph7_3 "Rhea list of ChEBI terms representing the major species at pH 7.3." +subsetdef: gocheck_do_not_annotate "Term not to be used for direct annotation" +subsetdef: gocheck_do_not_manually_annotate "Term not to be used for direct manual annotation" +subsetdef: goslim_agr "AGR slim" +subsetdef: goslim_aspergillus "Aspergillus GO slim" +subsetdef: goslim_candida "Candida GO slim" +subsetdef: goslim_chembl "ChEMBL protein targets summary" +subsetdef: goslim_drosophila "Drosophila GO slim" +subsetdef: goslim_flybase_ribbon "FlyBase Drosophila GO ribbon slim" +subsetdef: goslim_generic "Generic GO slim" +subsetdef: goslim_metagenomics "Metagenomics GO slim" +subsetdef: goslim_mouse "Mouse GO slim" +subsetdef: goslim_pir "PIR GO slim" +subsetdef: goslim_plant "Plant GO slim" +subsetdef: goslim_pombe "Fission yeast GO slim" +subsetdef: goslim_synapse "synapse GO slim" +subsetdef: goslim_yeast "Yeast GO slim" +subsetdef: prokaryote_subset "GO subset for prokaryotes" +synonymtypedef: syngo_official_label "label approved by the SynGO project" +synonymtypedef: systematic_synonym "Systematic synonym" EXACT +default-namespace: gene_ontology +ontology: go +property_value: has_ontology_root_term GO:0003674 +property_value: has_ontology_root_term GO:0005575 +property_value: has_ontology_root_term GO:0008150 +property_value: http://purl.org/dc/elements/1.1/description "The Gene Ontology (GO) provides a framework and set of concepts for describing the functions of gene products from all organisms." xsd:string +property_value: http://purl.org/dc/elements/1.1/title "Gene Ontology" xsd:string +property_value: http://purl.org/dc/terms/license http://creativecommons.org/licenses/by/4.0/ + +[Term] +id: GO:0000108 +name: obsolete repairosome +namespace: cellular_component +def: "OBSOLETE. A stable complex of proteins that carry out the DNA damage recognition and incision reactions characteristic of nucleotide excision repair (NER), such as DNA damage recognition, DNA helix unwinding, and endonucleolytic cleavage at sites flanking damaged DNA; includes TFIIH subunits and additional polypeptides; may form in the absence of DNA damage." [PMID:10681587, PMID:9852079] +comment: This term was made obsolete because 'repairosome' has fallen out of use in the literature, and the large complex described in the definition has not been confirmed to exist. The term has also confused annotators. +synonym: "repairosome" EXACT [] +is_obsolete: true +replaced_by: GO:0000109 + +[Term] +id: GO:0000109 +name: nucleotide-excision repair complex +namespace: cellular_component +def: "Any complex formed of proteins that act in nucleotide-excision repair." [PMID:10915862] +comment: Note that process information is included in the term and definition for the purpose of describing and distinguishing the complex. +subset: goslim_pir +synonym: "UvrB-UvrC complex" NARROW [PMID:12145219] +synonym: "UvrBC complex" NARROW [GOC:bhm, PMID:12145219] +is_a: GO:0032991 ! protein-containing complex +intersection_of: GO:0032991 ! protein-containing complex +intersection_of: capable_of_part_of GO:0006289 ! nucleotide-excision repair +relationship: part_of GO:0005634 ! nucleus + +[Term] +id: GO:0000114 +name: obsolete regulation of transcription involved in G1 phase of mitotic cell cycle +namespace: biological_process +def: "OBSOLETE. Any process that regulates transcription such that the target genes are transcribed as part of the G1 phase of the mitotic cell cycle." [GOC:dph, GOC:mah, GOC:tb] +comment: This term was made obsolete because it is unclear exactly what it means. It could mean either 'regulation of transcription during phase X' or 'regulation of transition between phase X and phase Y'. +synonym: "G1-specific transcription in mitotic cell cycle" RELATED [] +synonym: "regulation of transcription from RNA polymerase II promoter during G1 phase of cell cycle" EXACT [] +synonym: "regulation of transcription involved in G1 phase of mitotic cell cycle" EXACT [] +is_obsolete: true +consider: GO:0000083 +consider: GO:0006357 + +[Term] +id: GO:0000122 +name: negative regulation of transcription by RNA polymerase II +namespace: biological_process +alt_id: GO:0010553 +alt_id: GO:0045816 +def: "Any process that stops, prevents, or reduces the frequency, rate or extent of transcription mediated by RNA polymerase II." [GOC:go_curators, GOC:txnOH] +synonym: "down regulation of global transcription from RNA polymerase II promoter" RELATED [] +synonym: "down regulation of transcription from RNA polymerase II promoter" EXACT [] +synonym: "down-regulation of global transcription from RNA polymerase II promoter" RELATED [] +synonym: "down-regulation of transcription from RNA polymerase II promoter" EXACT [] +synonym: "downregulation of global transcription from RNA polymerase II promoter" RELATED [] +synonym: "downregulation of transcription from RNA polymerase II promoter" EXACT [] +synonym: "inhibition of global transcription from RNA polymerase II promoter" RELATED [] +synonym: "inhibition of transcription from RNA polymerase II promoter" EXACT [] +synonym: "negative regulation of gene-specific transcription from RNA polymerase II promoter" RELATED [] +synonym: "negative regulation of global transcription from Pol II promoter" RELATED [] +synonym: "negative regulation of transcription from Pol II promoter" EXACT [] +synonym: "negative regulation of transcription from RNA polymerase II promoter" EXACT [] +synonym: "negative regulation of transcription from RNA polymerase II promoter, global" RELATED [] +intersection_of: GO:0065007 ! biological regulation +intersection_of: negatively_regulates GO:0006366 ! transcription by RNA polymerase II + +[Typedef] +id: capable_of_part_of +name: capable of part of +namespace: external +xref: RO:0002216 + +[Typedef] +id: negatively_regulates +name: negatively regulates +namespace: external +xref: RO:0002212 +is_a: regulates ! regulates diff --git a/contract/src/test/resources/obo/test_prefixes.obo b/contract/src/test/resources/obo/test_prefixes.obo new file mode 100644 index 0000000000..2c4086c997 --- /dev/null +++ b/contract/src/test/resources/obo/test_prefixes.obo @@ -0,0 +1,12 @@ +format-version: 1.2 +idspace: owl http://www.w3.org/2002/07/owl# +idspace: rdf http://www.w3.org/1999/02/22-rdf-syntax-ns# +idspace: rdfs http://www.w3.org/2000/01/rdf-schema# +idspace: sw http://somewhere.org/ +idspace: xml http://www.w3.org/XML/1998/namespace +idspace: xsd http://www.w3.org/2001/XMLSchema# +ontology: foo + +[Term] +id: FOO:1234 +is_a: sw:MyClass From 13a2d43c04035e97ce94e380e9b0399ca2c70f06 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Fri, 20 Oct 2023 19:03:57 -0400 Subject: [PATCH 11/17] Ensure longest namespace match is used. Disallow prefixes that are substrings of OBO namespace. --- .../obo2owl/OBOFormatPrefixManager.java | 175 ++++++++++++++++++ .../org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 2 +- 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java new file mode 100644 index 0000000000..fe1065e9e3 --- /dev/null +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java @@ -0,0 +1,175 @@ +package org.obolibrary.obo2owl; + +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLRuntimeException; +import org.semanticweb.owlapi.model.PrefixManager; +import org.semanticweb.owlapi.util.StringComparator; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class OBOFormatPrefixManager implements PrefixManager { + + private final String OBO_NS = "http://purl.obolibrary.org/obo/"; + + @Nonnull + private StringComparator comparator = new StringComparator() { + + final Comparator comparer = Comparator.comparing(s -> -s.length()); + + @Override + public int compare(String o1, String o2) { + return comparer.compare(o1, o2); + } + + }; + + @Nonnull + private final TreeMap nsToPrefix; + @Nonnull + private final TreeMap prefixToNS; + + public OBOFormatPrefixManager(@Nullable PrefixManager pm) { + prefixToNS = new TreeMap<>(comparator); + nsToPrefix = new TreeMap<>(comparator); + if (pm != null) { + copyPrefixesFrom(pm); + } + } + + @Nonnull + @Override + public StringComparator getPrefixComparator() { + return this.comparator; + } + + @Override + public void setPrefixComparator(@Nonnull StringComparator comparator) { + this.comparator = comparator; + } + + @Nullable + @Override + public String getDefaultPrefix() { + return null; + } + + @Override + public boolean containsPrefixMapping(@Nonnull String prefixName) { + return prefixToNS.containsKey(prefixName); + } + + @Nullable + @Override + public String getPrefix(@Nonnull String prefixName) { + return prefixToNS.get(prefixName); + } + + @Nonnull + @Override + public Map getPrefixName2PrefixMap() { + return Collections.unmodifiableMap(prefixToNS); + } + + @Nonnull + @Override + public IRI getIRI(@Nonnull String prefixIRI) { + if (prefixIRI.startsWith("<")) { + return IRI.create(prefixIRI.substring(1, prefixIRI.length() - 1)); + } + int sep = prefixIRI.indexOf(':'); + if (sep == -1) { + if (getDefaultPrefix() != null) { + return IRI.create(getDefaultPrefix() + prefixIRI); + } else { + return IRI.create(prefixIRI); + } + } else { + String prefixName = prefixIRI.substring(0, sep + 1); + if (!containsPrefixMapping(prefixName)) { + throw new OWLRuntimeException( + "Prefix not registered for prefix name: " + prefixName); + } + String prefix = getPrefix(prefixName); + String localName = prefixIRI.substring(sep + 1); + return IRI.create(prefix, localName); + } + } + + @Nullable + @Override + public String getPrefixIRI(@Nonnull IRI iri) { + String iriString = iri.toString(); + Optional> mappingOpt = nsToPrefix.entrySet().stream().filter(e -> iriString.startsWith(e.getKey())).findFirst(); + if (mappingOpt.isPresent()) { + Map.Entry mapping = mappingOpt.get(); + String localId = iriString.substring(mapping.getKey().length()); + return mapping.getValue() + ":" + localId; + } else return null; + } + + @Nullable + @Override + public String getPrefixIRIIgnoreQName(@Nonnull IRI iri) { + return getPrefixIRI(iri); + } + + @Nonnull + @Override + public Set getPrefixNames() { + return prefixToNS.keySet(); + } + + @Override + public void setDefaultPrefix(@Nullable String defaultPrefix) { + } + + @Override + public void setPrefix(@Nonnull String prefixName, @Nonnull String prefix) { + if (!prefixName.isEmpty() && !prefixName.equals(":") && !OBO_NS.startsWith(prefix)) { + System.out.println(prefixName + " ::: " + prefix); + String cleanPrefixName = prefixName; + if (prefixName.endsWith(":")) { + cleanPrefixName = prefixName.substring(0, prefixName.length() - 1); + } + prefixToNS.put(cleanPrefixName, prefix); + nsToPrefix.put(prefix, cleanPrefixName); + } + } + + @Override + public void copyPrefixesFrom(@Nonnull PrefixManager from) { + copyPrefixesFrom(from.getPrefixName2PrefixMap()); + } + + @Override + public void copyPrefixesFrom(@Nonnull Map from) { + for (Map.Entry e : from.entrySet()) { + String prefix = e.getKey(); + if (!prefix.isEmpty()) { + setPrefix(e.getKey(), e.getValue()); + } + } + } + + @Override + public void unregisterNamespace(@Nonnull String namespace) { + List toRemove = new ArrayList<>(); + for (Map.Entry e : prefixToNS.entrySet()) { + if (e.getValue().equals(namespace)) { + toRemove.add(e.getKey()); + } + } + for (String s : toRemove) { + prefixToNS.remove(s); + } + nsToPrefix.remove(namespace); + } + + @Override + public void clear() { + prefixToNS.clear(); + nsToPrefix.clear(); + } +} diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index 2695b0805c..88cbcaa9a9 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -233,7 +233,7 @@ public void setObodoc(@Nonnull OBODoc obodoc) { } public void setPrefixManager(@Nonnull PrefixManager manager) { - this.prefixManager = manager; + this.prefixManager = new OBOFormatPrefixManager(manager); } /** From 7cf37cadb0ebde947600e85e27b60049a251bca8 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Thu, 1 Feb 2024 14:06:02 -0500 Subject: [PATCH 12/17] If a prefix is declared in idspaces, don't use # separator for "non-canonical" ids. --- .../org/obolibrary/obo2owl/OWLAPIObo2Owl.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java index c7f49c9754..cd027d3a5a 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIObo2Owl.java @@ -1715,12 +1715,15 @@ public IRI oboIdToIRI_load(@Nonnull String id, boolean oboInOwlDefault) { String localId; if (idParts.length > 1) { // Prefixed-ID (canonical or not) localId = idParts[1]; - uriPrefix = idSpaceMap.getOrDefault(idParts[0], DEFAULT_IRI_PREFIX + idParts[0] + '_'); - - // Non-canonical prefixed IDs use a '#' separator - // TODO - recognize all non-canonical prefixed IDs - if (localId.contains("_")) { - uriPrefix += "#"; + if (idSpaceMap.containsKey(idParts[0])) { + uriPrefix = idSpaceMap.get(idParts[0]); + } else { + uriPrefix = DEFAULT_IRI_PREFIX + idParts[0] + '_'; + // Non-canonical prefixed IDs use a '#' separator + // TODO - recognize all non-canonical prefixed IDs + if (localId.contains("_")) { + uriPrefix += "#"; + } } } else { // Unprefixed-ID // Special case for relation xrefs (5.9.3. Special Rules for Relations) From 47dca605e9921768bd081238b5e16e63672fcef0 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Thu, 1 Feb 2024 14:06:57 -0500 Subject: [PATCH 13/17] Drop conflicting id annotations rather than stuff into owl-axioms header. --- .../src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index 88cbcaa9a9..d6df3635a4 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -889,8 +889,8 @@ && isMetadataTag(prop)) { if (!valueString.isEmpty()) { if (tag == OboFormatTag.TAG_ID) { if (!frame.getId().equals(value)) { - warn("Conflicting id definitions: 1) " + frame.getId() + " 2)" + value); - return false; + warn("Dropping conflicting id definition: 1) " + frame.getId() + " 2)" + value); + return true; } return true; } From 672eb6e828ea498945b6dbedbd7000193f3ddd8f Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Fri, 2 Feb 2024 09:50:45 -0500 Subject: [PATCH 14/17] Corrected key comparison in OBOFormatPrefixManager. Exclude OBO IRIs from idspaces. --- .../obolibrary/oboformat/PrefixesTest.java | 26 +++++++++++++++-- .../test/resources/obo/test_obo_prefix.obo | 25 +++++++++++++++++ .../src/test/resources/obo/test_prefixes.obo | 5 ++++ .../obo2owl/OBOFormatPrefixManager.java | 14 +++++++--- .../org/obolibrary/obo2owl/OWLAPIOwl2Obo.java | 28 +++++++++++-------- 5 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 contract/src/test/resources/obo/test_obo_prefix.obo diff --git a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java index 5cea48cbee..00ec8e86a2 100644 --- a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java +++ b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java @@ -3,10 +3,12 @@ import org.junit.jupiter.api.Test; import org.semanticweb.owlapi.api.test.baseclasses.TestBase; import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.formats.OBODocumentFormat; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.search.EntitySearcher; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -15,17 +17,20 @@ public class PrefixesTest extends TestBase { @Test - void testPrefixesRoundtrip() throws OWLOntologyStorageException { + void testPrefixesRoundtrip() throws OWLOntologyStorageException, IOException { OWLDataFactory factory = OWLManager.getOWLDataFactory(); OWLAnnotationProperty termReplacedBy = factory.getOWLAnnotationProperty(IRI.create("http://purl.obolibrary.org/obo/IAO_0100001")); OWLAnnotationProperty consider = factory.getOWLAnnotationProperty(IRI.create("http://www.geneontology.org/formats/oboInOwl#consider")); OWLOntology oboOnt = loadOntology("obo/test_prefixes.obo", OWLManager.createOWLOntologyManager()); - assertTrue(oboOnt.containsEntityInSignature(factory.getOWLClass(IRI.create("http://purl.obolibrary.org/obo/FOO_1234")))); - assertTrue(oboOnt.containsEntityInSignature(factory.getOWLClass(IRI.create("http://somewhere.org/MyClass")))); + assertTrue(oboOnt.containsClassInSignature(IRI.create("http://purl.obolibrary.org/obo/FOO_1234"))); + assertTrue(oboOnt.containsClassInSignature(IRI.create("http://somewhere.org/MyClass"))); + assertFalse(oboOnt.containsClassInSignature(IRI.create("https://example.org/myns/#ABC_123"))); + assertTrue(oboOnt.containsClassInSignature(IRI.create("https://example.org/myns/ABC_123"))); Map prefixMap = oboOnt.getOWLOntologyManager().getOntologyFormat(oboOnt).asPrefixOWLOntologyFormat().getPrefixName2PrefixMap(); assertEquals("http://somewhere.org/", prefixMap.get("sw:")); ByteArrayOutputStream stream = new ByteArrayOutputStream(); oboOnt.getOWLOntologyManager().saveOntology(oboOnt, stream); + stream.close(); String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); assertTrue(roundtripOBO.contains("idspace: sw http://somewhere.org/")); assertTrue(roundtripOBO.contains("[Term]\nid: FOO:1234\nis_a: sw:MyClass")); @@ -45,9 +50,24 @@ void testPrefixesRoundtrip() throws OWLOntologyStorageException { "Values for replaced_by on alt_ids should be IRIs"); ByteArrayOutputStream stream2 = new ByteArrayOutputStream(); replacementsOnt.getOWLOntologyManager().saveOntology(replacementsOnt, stream2); + stream2.close(); String roundtripReplacementsOnt = new String(stream2.toByteArray(), StandardCharsets.UTF_8); assertFalse(roundtripReplacementsOnt.contains("idspace:")); assertTrue(roundtripReplacementsOnt.contains("replaced_by: GO:0000109")); } + @Test + void testHandlingOfDeclaredOBOPrefix() throws OWLOntologyStorageException, IOException, OWLOntologyCreationException { + OWLOntology oboOnt = loadOntology("obo/test_obo_prefix.obo", OWLManager.createOWLOntologyManager()); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + oboOnt.getOWLOntologyManager().saveOntology(oboOnt, stream); + stream.close(); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8).substring(0, 2000); + assertFalse(roundtripOBO.contains("obo:")); + assertFalse(roundtripOBO.contains("obo ")); + assertFalse(roundtripOBO.contains("ex:MMyClass"), "The longest available namespace match should be used"); + assertFalse(roundtripOBO.contains("owl-axioms:")); + + } + } diff --git a/contract/src/test/resources/obo/test_obo_prefix.obo b/contract/src/test/resources/obo/test_obo_prefix.obo new file mode 100644 index 0000000000..ad3fba035a --- /dev/null +++ b/contract/src/test/resources/obo/test_obo_prefix.obo @@ -0,0 +1,25 @@ +format-version: 1.2 +idspace: ex http://example.org/ +idspace: ex1 http://example.org/M +idspace: obo http://purl.obolibrary.org/obo/ +ontology: foo + +[Term] +id: FOO:1234 +name: the 1234 class +def: "A very important concept." +is_a: ex:MyClass + +[Term] +id: ex:MyClass +name: my ex class + +[Term] +id: ex1:MyClass +name: my ex1 class +relationship: capable_of_part_of FOO:1234 + +[Typedef] +id: capable_of_part_of +name: capable of part of +xref: RO:0002216 diff --git a/contract/src/test/resources/obo/test_prefixes.obo b/contract/src/test/resources/obo/test_prefixes.obo index 2c4086c997..afd9cc1878 100644 --- a/contract/src/test/resources/obo/test_prefixes.obo +++ b/contract/src/test/resources/obo/test_prefixes.obo @@ -3,6 +3,7 @@ idspace: owl http://www.w3.org/2002/07/owl# idspace: rdf http://www.w3.org/1999/02/22-rdf-syntax-ns# idspace: rdfs http://www.w3.org/2000/01/rdf-schema# idspace: sw http://somewhere.org/ +idspace: myns https://example.org/myns/ idspace: xml http://www.w3.org/XML/1998/namespace idspace: xsd http://www.w3.org/2001/XMLSchema# ontology: foo @@ -10,3 +11,7 @@ ontology: foo [Term] id: FOO:1234 is_a: sw:MyClass + +[Term] +id: myns:ABC_123 +is_a: sw:MyClass diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java index fe1065e9e3..101234e543 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OBOFormatPrefixManager.java @@ -16,11 +16,13 @@ public class OBOFormatPrefixManager implements PrefixManager { @Nonnull private StringComparator comparator = new StringComparator() { - final Comparator comparer = Comparator.comparing(s -> -s.length()); + private final Comparator alphabetical = Comparator.comparing(s -> s); + private final Comparator byLength = Comparator.comparing(s -> -s.length()); + private final Comparator byBoth = byLength.thenComparing(alphabetical); @Override public int compare(String o1, String o2) { - return comparer.compare(o1, o2); + return byBoth.compare(o1, o2); } }; @@ -125,10 +127,14 @@ public Set getPrefixNames() { public void setDefaultPrefix(@Nullable String defaultPrefix) { } + /** + * @param prefixName name The prefix name (must end with a colon) + * @param prefix The prefix. This prefix manager does not accept prefixes that overlap with + * the default OBO namespace. + */ @Override public void setPrefix(@Nonnull String prefixName, @Nonnull String prefix) { - if (!prefixName.isEmpty() && !prefixName.equals(":") && !OBO_NS.startsWith(prefix)) { - System.out.println(prefixName + " ::: " + prefix); + if (!prefixName.isEmpty() && !prefixName.equals(":") && !OBO_NS.startsWith(prefix) && !prefix.startsWith(OBO_NS)) { String cleanPrefixName = prefixName; if (prefixName.endsWith(":")) { cleanPrefixName = prefixName.substring(0, prefixName.length() - 1); diff --git a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java index d6df3635a4..cf45247344 100644 --- a/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java +++ b/oboformat/src/main/java/org/obolibrary/obo2owl/OWLAPIOwl2Obo.java @@ -1,5 +1,6 @@ package org.obolibrary.obo2owl; +import static org.obolibrary.obo2owl.Obo2OWLConstants.DEFAULT_IRI_PREFIX; import static org.semanticweb.owlapi.search.EntitySearcher.getAnnotationObjects; import static org.semanticweb.owlapi.util.OWLAPIPreconditions.verifyNotNull; @@ -46,9 +47,9 @@ public class OWLAPIOwl2Obo { */ private static final Logger LOG = LoggerFactory.getLogger(OWLAPIOwl2Obo.class); private static final String IRI_CLASS_SYNONYMTYPEDEF = - Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_synonymtypedef"; + DEFAULT_IRI_PREFIX + "IAO_synonymtypedef"; private static final String IRI_CLASS_SUBSETDEF = - Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_subsetdef"; + DEFAULT_IRI_PREFIX + "IAO_subsetdef"; /** * The absoulte url pattern. */ @@ -141,7 +142,7 @@ protected final void init() { public OWLAPIOwl2Obo(@Nonnull OWLOntologyManager translationManager) { manager = translationManager; fac = manager.getOWLDataFactory(); - init(); + //init(); } /** @@ -1045,7 +1046,7 @@ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotati Clause clause = new Clause(OboFormatTag.TAG_PROPERTY_VALUE.getTag()); String propId = getIdentifier(prop); addQualifiers(clause, qualifiers, this.prefixManager); - if (!propId.equals("shorthand")) { + if (!prop.getIRI().equals(Obo2OWLVocabulary.IRI_OIO_shorthand.iri)) { clause.addValue(propId); if (annVal instanceof OWLLiteral) { OWLLiteral owlLiteral = (OWLLiteral) annVal; @@ -1723,10 +1724,10 @@ public static String getIdentifierFromObject(OWLObject obj, @Nonnull OWLOntology Set axioms = ont.getAnnotationAssertionAxioms(entity.getIRI()); for (OWLAnnotationAssertionAxiom ax : axioms) { - String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont, pm); + IRI propId = ax.getProperty().getIRI(); // see BFOROXrefTest // 5.9.3. Special Rules for Relations - if (propId != null && propId.equals("shorthand")) { + if (propId.equals(Obo2OWLVocabulary.IRI_OIO_shorthand.iri)) { OWLAnnotationValue value = ax.getValue(); if (value instanceof OWLLiteral) { return ((OWLLiteral) value).getLiteral(); @@ -1769,9 +1770,14 @@ public static String getIdentifier(@Nullable IRI iriId, @Nonnull PrefixManager p if (iriId == null) { return null; } - String curie = pm.getPrefixIRIIgnoreQName(iriId); - if (curie != null && !curie.startsWith(":")) { - return curie; + // If the iri is OBO-style, don't use the prefix manager. + // There is a lot of special-case legacy behavior that + // should not be broken. + if (!iriId.toString().startsWith(DEFAULT_IRI_PREFIX)) { + String curie = pm.getPrefixIRIIgnoreQName(iriId); + if (curie != null && !curie.startsWith(":")) { + return curie; + } } // canonical IRIs // if (iri.startsWith("http://purl.obolibrary.org/obo/")) { @@ -1875,8 +1881,8 @@ public static String owlObjectToTag(OWLObject obj, @Nonnull PrefixManager pm) { String tag = ANNOTATIONPROPERTYMAP.get(iri); if (tag == null) { // hard coded values for legacy annotation properties: (TEMPORARY) - if (iri.startsWith(Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_")) { - String legacyId = iri.replace(Obo2OWLConstants.DEFAULT_IRI_PREFIX, ""); + if (iri.startsWith(DEFAULT_IRI_PREFIX + "IAO_")) { + String legacyId = iri.replace(DEFAULT_IRI_PREFIX, ""); if (legacyId.equals("IAO_xref")) { return OboFormatTag.TAG_XREF.getTag(); } From fb66f71beb8075dcfcdefc604327cd11e97b1099 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Fri, 2 Feb 2024 09:54:26 -0500 Subject: [PATCH 15/17] Remove debugging code. --- .../src/test/java/org/obolibrary/oboformat/PrefixesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java index 00ec8e86a2..fc6210006c 100644 --- a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java +++ b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java @@ -62,7 +62,7 @@ void testHandlingOfDeclaredOBOPrefix() throws OWLOntologyStorageException, IOExc ByteArrayOutputStream stream = new ByteArrayOutputStream(); oboOnt.getOWLOntologyManager().saveOntology(oboOnt, stream); stream.close(); - String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8).substring(0, 2000); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); assertFalse(roundtripOBO.contains("obo:")); assertFalse(roundtripOBO.contains("obo ")); assertFalse(roundtripOBO.contains("ex:MMyClass"), "The longest available namespace match should be used"); From 53a73a1d7085af22614b609e4513d55b403ff1a3 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Thu, 8 Feb 2024 14:58:25 -0500 Subject: [PATCH 16/17] Don't inject default semweb prefixes into OBO document format. --- .../owlapi/formats/OBODocumentFormat.java | 7 ++++-- .../obolibrary/oboformat/PrefixesTest.java | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java b/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java index 8ef3b8a380..66f6475b8a 100644 --- a/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java +++ b/api/src/main/java/org/semanticweb/owlapi/formats/OBODocumentFormat.java @@ -14,8 +14,6 @@ import javax.annotation.Nonnull; -import org.semanticweb.owlapi.model.OWLDocumentFormatImpl; - /** * @author Matthew Horridge, The University Of Manchester, Bio-Health * Informatics Group @@ -30,6 +28,11 @@ public class OBODocumentFormat extends PrefixDocumentFormatImpl { public static final String VALIDATION = "obo.validation"; private static final long serialVersionUID = 40000L; + public OBODocumentFormat() { + super(); + this.clear(); + } + @Nonnull @Override public String getKey() { diff --git a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java index fc6210006c..c65c8c1a91 100644 --- a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java +++ b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java @@ -67,7 +67,32 @@ void testHandlingOfDeclaredOBOPrefix() throws OWLOntologyStorageException, IOExc assertFalse(roundtripOBO.contains("obo ")); assertFalse(roundtripOBO.contains("ex:MMyClass"), "The longest available namespace match should be used"); assertFalse(roundtripOBO.contains("owl-axioms:")); + } + @Test + void testOBOFormatShouldNotInjectPrefixes() throws OWLOntologyStorageException, IOException { + OWLOntology oboOnt = loadOntology("obo/test_obo_prefix.obo", OWLManager.createOWLOntologyManager()); + OWLOntologyManager manager = oboOnt.getOWLOntologyManager(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + OWLDocumentFormat format = new OBODocumentFormat(); + format.asPrefixOWLOntologyFormat().copyPrefixesFrom(manager.getOntologyFormat(oboOnt).asPrefixOWLOntologyFormat()); + manager.saveOntology(oboOnt, format, stream); + stream.close(); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); + assertFalse(roundtripOBO.contains("idspace: rdf")); + } + + @Test + void testOBOFormatShouldPreventOBOPrefixes() throws OWLOntologyStorageException, IOException { + OWLOntology oboOnt = loadOntology("obo/test_obo_prefix.obo", OWLManager.createOWLOntologyManager()); + OWLOntologyManager manager = oboOnt.getOWLOntologyManager(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + OWLDocumentFormat format = manager.getOntologyFormat(oboOnt); + format.asPrefixOWLOntologyFormat().setPrefix("GO", "http://purl.obolibrary.org/obo/GX_"); + manager.saveOntology(oboOnt, format, stream); + stream.close(); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); + assertFalse(roundtripOBO.contains("idspace: GO")); } } From 7b1f5852874c7aef09a9b2425c76372b61104c45 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Sun, 28 Apr 2024 21:30:28 -0400 Subject: [PATCH 17/17] Test prefixes aren't injected. --- .../org/obolibrary/oboformat/PrefixesTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java index c65c8c1a91..8a3cf7ddf7 100644 --- a/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java +++ b/contract/src/test/java/org/obolibrary/oboformat/PrefixesTest.java @@ -70,11 +70,26 @@ void testHandlingOfDeclaredOBOPrefix() throws OWLOntologyStorageException, IOExc } @Test - void testOBOFormatShouldNotInjectPrefixes() throws OWLOntologyStorageException, IOException { + void testOBOFormatShouldNotInjectPrefixesInConstructedDocFormat() throws OWLOntologyStorageException, IOException { OWLOntology oboOnt = loadOntology("obo/test_obo_prefix.obo", OWLManager.createOWLOntologyManager()); OWLOntologyManager manager = oboOnt.getOWLOntologyManager(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); OWLDocumentFormat format = new OBODocumentFormat(); + String defaultNamespace = format.asPrefixOWLOntologyFormat().getDefaultPrefix(); + format.asPrefixOWLOntologyFormat().copyPrefixesFrom(manager.getOntologyFormat(oboOnt).asPrefixOWLOntologyFormat()); + format.asPrefixOWLOntologyFormat().setDefaultPrefix(defaultNamespace); + manager.saveOntology(oboOnt, format, stream); + stream.close(); + String roundtripOBO = new String(stream.toByteArray(), StandardCharsets.UTF_8); + assertFalse(roundtripOBO.contains("idspace: rdf")); + } + + @Test + void testOBOFormatShouldNotInjectPrefixesInLoadedDocFormat() throws OWLOntologyStorageException, IOException { + OWLOntology oboOnt = loadOntology("obo/test_obo_prefix.obo", OWLManager.createOWLOntologyManager()); + OWLOntologyManager manager = oboOnt.getOWLOntologyManager(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + OWLDocumentFormat format = manager.getOntologyFormat(oboOnt); format.asPrefixOWLOntologyFormat().copyPrefixesFrom(manager.getOntologyFormat(oboOnt).asPrefixOWLOntologyFormat()); manager.saveOntology(oboOnt, format, stream); stream.close();