From 59b5b78e6c62c1663e2645a299c5057e915b49fa Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Fri, 20 Dec 2024 19:11:39 +0000 Subject: [PATCH] Add support for `predicate_type`. Recognise the `predicate_type` slot in mappings and mapping sets. Check that it does not contain the illegal values `rdfs literal` and `composed entity expression`. Add a `predicate_type==` filter in SSSOM/T. When producing OWL axioms from mappings in "direct" mode, use the value of the `predicate_type` slot, if present, to determine the type of axiom to generate. --- .../incenp/obofoundry/sssom/Validator.java | 6 +++++ .../obofoundry/sssom/model/Mapping.java | 3 +++ .../obofoundry/sssom/model/MappingSet.java | 4 ++++ .../obofoundry/sssom/ValidatorTest.java | 16 ++++++++++++- .../sets/test-predicate-types.sssom.tsv | 10 ++++++++ .../sssom/transform/parser/SSSOMTransform.g4 | 1 + .../sssom/owl/DirectAxiomGenerator.java | 24 ++++++++++++++++++- .../sssom/transform/SSSOMTransformReader.java | 4 ++++ 8 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 core/src/test/resources/sets/test-predicate-types.sssom.tsv diff --git a/core/src/main/java/org/incenp/obofoundry/sssom/Validator.java b/core/src/main/java/org/incenp/obofoundry/sssom/Validator.java index a32c405a..3efa21fc 100644 --- a/core/src/main/java/org/incenp/obofoundry/sssom/Validator.java +++ b/core/src/main/java/org/incenp/obofoundry/sssom/Validator.java @@ -38,6 +38,7 @@ public class Validator { public static final String MISSING_OBJECT_ID = "Missing object_id"; public static final String MISSING_PREDICATE = "Missing predicate_id"; public static final String MISSING_JUSTIFICATION = "Missing mapping_justification"; + public static final String INVALID_PREDICATE_TYPE = "Invalid predicate_type"; /** * Validates an individual mapping. This method checks that the slots that are @@ -75,6 +76,11 @@ public String validate(Mapping mapping) { return MISSING_JUSTIFICATION; } + if ( mapping.getPredicateType() == EntityType.RDFS_LITERAL + || mapping.getPredicateType() == EntityType.COMPOSED_ENTITY_EXPRESSION ) { + return INVALID_PREDICATE_TYPE; + } + return null; } } diff --git a/core/src/main/lombok/org/incenp/obofoundry/sssom/model/Mapping.java b/core/src/main/lombok/org/incenp/obofoundry/sssom/model/Mapping.java index 1d0187f7..f1b86686 100644 --- a/core/src/main/lombok/org/incenp/obofoundry/sssom/model/Mapping.java +++ b/core/src/main/lombok/org/incenp/obofoundry/sssom/model/Mapping.java @@ -102,6 +102,9 @@ public class Mapping { @JsonProperty("object_source_version") private String objectSourceVersion; + @JsonProperty("predicate_type") + private EntityType predicateType; + @JsonProperty("mapping_provider") @URI private String mappingProvider; diff --git a/core/src/main/lombok/org/incenp/obofoundry/sssom/model/MappingSet.java b/core/src/main/lombok/org/incenp/obofoundry/sssom/model/MappingSet.java index e8d5eb6a..f7776697 100644 --- a/core/src/main/lombok/org/incenp/obofoundry/sssom/model/MappingSet.java +++ b/core/src/main/lombok/org/incenp/obofoundry/sssom/model/MappingSet.java @@ -81,6 +81,10 @@ public class MappingSet { @Propagatable private String objectSourceVersion; + @JsonProperty("predicate_type") + @Propagatable + private EntityType predicateType; + @JsonProperty("mapping_provider") @Propagatable @URI diff --git a/core/src/test/java/org/incenp/obofoundry/sssom/ValidatorTest.java b/core/src/test/java/org/incenp/obofoundry/sssom/ValidatorTest.java index dc12f629..a4f8e3bd 100644 --- a/core/src/test/java/org/incenp/obofoundry/sssom/ValidatorTest.java +++ b/core/src/test/java/org/incenp/obofoundry/sssom/ValidatorTest.java @@ -27,7 +27,7 @@ public class ValidatorTest { @Test - void testInvalidMappings() throws SSSOMFormatException, IOException { + void testMappingsWithMissingRequiredSlots() throws SSSOMFormatException, IOException { TSVReader reader = new TSVReader("src/test/resources/sets/test-missing-required-slots.sssom.tsv"); reader.setValidationEnabled(false); // So we can check each mapping ourselves MappingSet ms = reader.read(); @@ -41,4 +41,18 @@ void testInvalidMappings() throws SSSOMFormatException, IOException { Assertions.assertEquals(expectedErrors[i], v.validate(ms.getMappings().get(i))); } } + + @Test + void testMappingsWithInvalidPredicateTypes() throws SSSOMFormatException, IOException { + TSVReader reader = new TSVReader("src/test/resources/sets/test-predicate-types.sssom.tsv"); + reader.setValidationEnabled(false); + MappingSet ms = reader.read(); + + String[] expectedErrors = { null, null, Validator.INVALID_PREDICATE_TYPE, Validator.INVALID_PREDICATE_TYPE }; + + Validator v = new Validator(); + for ( int i = 0, n = ms.getMappings().size(); i < n; i++ ) { + Assertions.assertEquals(expectedErrors[i], v.validate(ms.getMappings().get(i))); + } + } } diff --git a/core/src/test/resources/sets/test-predicate-types.sssom.tsv b/core/src/test/resources/sets/test-predicate-types.sssom.tsv new file mode 100644 index 00000000..2f7feffe --- /dev/null +++ b/core/src/test/resources/sets/test-predicate-types.sssom.tsv @@ -0,0 +1,10 @@ +#curie_map: +# COMENT: https://example.com/entities/ +# ORGENT: https://example.org/entities/ +#mapping_set_id: https://example.org/sets/test-predicate-types +#license: https://creativecommons.org/licenses/by/4.0/ +subject_id subject_label predicate_id object_id object_label mapping_justification predicate_type +ORGENT:0001 alice skos:closeMatch COMENT:0011 alpha semapv:ManualMappingCuration owl object property +ORGENT:0002 bob skos:closeMatch COMENT:0012 beta semapv:ManualMappingCuration owl annotation property +ORGENT:0004 daphne skos:closeMatch COMENT:0014 delta semapv:ManualMappingCuration rdfs literal +ORGENT:0005 eve skos:closeMatch COMENT:0015 epsilon semapv:ManualMappingCuration composed entity expression diff --git a/ext/src/main/antlr4/org/incenp/obofoundry/sssom/transform/parser/SSSOMTransform.g4 b/ext/src/main/antlr4/org/incenp/obofoundry/sssom/transform/parser/SSSOMTransform.g4 index ffc9c6ca..9e5f7ecc 100644 --- a/ext/src/main/antlr4/org/incenp/obofoundry/sssom/transform/parser/SSSOMTransform.g4 +++ b/ext/src/main/antlr4/org/incenp/obofoundry/sssom/transform/parser/SSSOMTransform.g4 @@ -99,6 +99,7 @@ cardField : 'mapping_cardinality' | 'cardinality'; entField : 'object_type' | 'subject_type' + | 'predicate_type' ; idValue : CURIE diff --git a/ext/src/main/java/org/incenp/obofoundry/sssom/owl/DirectAxiomGenerator.java b/ext/src/main/java/org/incenp/obofoundry/sssom/owl/DirectAxiomGenerator.java index 496e1eb0..6b848c7e 100644 --- a/ext/src/main/java/org/incenp/obofoundry/sssom/owl/DirectAxiomGenerator.java +++ b/ext/src/main/java/org/incenp/obofoundry/sssom/owl/DirectAxiomGenerator.java @@ -21,6 +21,7 @@ import java.util.HashSet; import java.util.Set; +import org.incenp.obofoundry.sssom.model.EntityType; import org.incenp.obofoundry.sssom.model.Mapping; import org.incenp.obofoundry.sssom.transform.IMappingTransformer; import org.semanticweb.owlapi.model.IRI; @@ -74,10 +75,31 @@ public DirectAxiomGenerator(OWLOntology ontology) { public OWLAxiom transform(Mapping mapping) { OWLAxiom axiom = null; String predicate = mapping.getPredicateId(); + EntityType predicateType = mapping.getPredicateType(); IRI subject = IRI.create(mapping.getSubjectId()); IRI object = IRI.create(mapping.getObjectId()); - if ( predicate.equals(OWL_EQUIVALENT_CLASS) ) { + /* + * The type of axiom to generate is dictated by the type of the predicate, which + * we obtain from (by order of precedence): + * + * (1) the mapping itself, if it has a `predicate_type` slot set to either `owl + * annotation property` or `owl object property`; + * + * (2) some built-in knowledge for a handful of predicates (owl:equivalentClass, + * rdfs:subClassOf, and the predicates listed in ANNOTATION_PREDICATES); + * + * (3) the helper ontology, it it declares an annotation or object property with + * a matching IRI. + */ + + if ( predicateType == EntityType.OWL_ANNOTATION_PROPERTY ) { + axiom = factory.getOWLAnnotationAssertionAxiom(factory.getOWLAnnotationProperty(IRI.create(predicate)), + subject, object); + } else if ( predicateType == EntityType.OWL_OBJECT_PROPERTY ) { + axiom = factory.getOWLSubClassOfAxiom(factory.getOWLClass(subject), factory.getOWLObjectSomeValuesFrom( + factory.getOWLObjectProperty(IRI.create(predicate)), factory.getOWLClass(object))); + } else if ( predicate.equals(OWL_EQUIVALENT_CLASS) ) { axiom = factory.getOWLEquivalentClassesAxiom(factory.getOWLClass(object), factory.getOWLClass(object)); } else if ( predicate.equals(RDFS_SUBCLASS_OF) ) { axiom = factory.getOWLSubClassOfAxiom(factory.getOWLClass(subject), factory.getOWLClass(object)); diff --git a/ext/src/main/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReader.java b/ext/src/main/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReader.java index 71463fb7..cacbcfef 100644 --- a/ext/src/main/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReader.java +++ b/ext/src/main/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReader.java @@ -989,6 +989,10 @@ public IMappingFilter visitEntityTypeFilterItem(SSSOMTransformParser.EntityTypeF case "subject_type": filter = (mapping) -> mapping.getSubjectType() == et; break; + + case "predicate_type": + filter = (mapping) -> mapping.getPredicateType() == et; + break; } return addFilter(new NamedFilter(String.format("%s==%s", fieldName, value), filter));