diff --git a/shapechange-app/src/docs/asciidoc/targets/ontology/UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc b/shapechange-app/src/docs/asciidoc/targets/ontology/UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc index de42b0cad..bae30ec67 100644 --- a/shapechange-app/src/docs/asciidoc/targets/ontology/UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc +++ b/shapechange-app/src/docs/asciidoc/targets/ontology/UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc @@ -377,6 +377,22 @@ This rule implements the conversion of enumerations as defined by ISO The enumeration corresponds to an RDFS datatype that specifies the restricted list of literals using an owl:oneOf declaration. +By default, the owl:oneOf declaration is added to the named RDFS +datatype itself. The OWL 2 mapping to RDF does not read this as an +enumeration of literals: an OWL 2 datatype is given a value space by a +datatype definition, which in RDF is an owl:equivalentClass from the +named datatype to an anonymous data range (see the +https://www.w3.org/TR/owl2-mapping-to-rdf/#Translation_of_Axioms_without_Annotations[OWL 2 Mapping to RDF Graphs, Section 2.1, Table 1]). +With target parameter +xref:./UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc#iso191502EnumerationAsDatatypeDefinition[_iso191502EnumerationAsDatatypeDefinition_] +set to _true_, the enumeration is encoded in this form: + +[source,turtle,linenumbers] +---------- +ex:Colour a rdfs:Datatype ; + owl:equivalentClass [ a rdfs:Datatype ; owl:oneOf ( "red" "green" ) ] . +---------- + [[rule-owl-cls-iso191502IsAbstract]] ==== rule-owl-cls-iso191502IsAbstract @@ -1119,6 +1135,44 @@ Applies to Rule(s): _none_ - this is a common target parameter +[[iso191502EnumerationAsDatatypeDefinition]] +==== iso191502EnumerationAsDatatypeDefinition + +(since v4.1.0) + +Type: Boolean + +Default Value: _false_ + +Explanation: + +If _true_, an enumeration is encoded as an OWL 2 datatype definition +(https://www.w3.org/TR/owl2-syntax/#Datatype_Definitions[OWL 2 +Structural Specification, Section 9.4]): the named RDFS datatype is +owl:equivalentClass to an anonymous RDFS datatype, which carries the +owl:oneOf list of the enumeration's literals. OWL 2 tools then see the +enumeration as `DatatypeDefinition( DT DataOneOf( ... ) )`, as OWL 2 DL +requires of a datatype that is not built in +(https://www.w3.org/TR/owl2-syntax/#The_Restrictions_on_the_Axiom_Closure[OWL +2 Structural Specification, Section 11.2]). By default, the owl:oneOf +list is added to the named datatype itself, which the OWL API, for +example, reads as an empty enumeration of individuals. + +This does not hold if a stereotype or type conversion parameter adds +rdfs:subClassOf to the enumeration. OWL 2 has no subclass relation +between datatypes, so the datatype is then also used as a class, and +the OWL API, for example, again reads an empty enumeration of +individuals. + +An enumeration without any literals is encoded as a named RDFS datatype +without a definition, whether or not this parameter is set, because +DataOneOf needs at least one literal. Such a datatype is not valid in +OWL 2 DL. + +Applies to Rule(s): + +* xref:./UML_to_RDF_OWL_based_on_ISO_IS_19150_2.adoc#rule-owl-cls-iso191502Enumeration[rule-owl-cls-iso191502Enumeration] + [[language]] ==== language diff --git a/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test.ttl b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test.ttl new file mode 100644 index 000000000..fa1e0e187 --- /dev/null +++ b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test.ttl @@ -0,0 +1,47 @@ +@prefix ex: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix skos: . +@prefix xsd: . + +ex:Road.name rdf:type owl:DatatypeProperty; + rdfs:domain ex:Road; + rdfs:range xsd:string . + +ex:Road rdf:type owl:Class; + rdfs:subClassOf [ rdf:type owl:Restriction; + owl:onDataRange ex:ExampleEnumeration; + owl:onProperty ex:Road.surface; + owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger + ]; + rdfs:subClassOf [ rdf:type owl:Restriction; + owl:onDataRange xsd:string; + owl:onProperty ex:Road.name; + owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger + ]; + rdfs:subClassOf [ rdf:type owl:Restriction; + owl:onClass ex:ExampleCodeList; + owl:onProperty ex:Road.category; + owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger + ] . + +ex:Road.category rdf:type owl:ObjectProperty; + rdfs:domain ex:Road; + rdfs:range ex:ExampleCodeList . + +ex:ExampleCodeList rdf:type owl:Class; + rdfs:subClassOf skos:Concept . + +ex:ExampleEnumeration + rdf:type rdfs:Datatype; + owl:equivalentClass [ rdf:type rdfs:Datatype; + owl:oneOf ( "alpha" "beta" "gamma" ) + ] . + +ex:Road.surface rdf:type owl:DatatypeProperty; + rdfs:domain ex:Road; + rdfs:range ex:ExampleEnumeration . + + + rdf:type owl:Ontology . diff --git a/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test_codes.ttl b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test_codes.ttl new file mode 100644 index 000000000..00c226026 --- /dev/null +++ b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference/owl/INPUT/test_codes.ttl @@ -0,0 +1,23 @@ +@prefix dct: . +@prefix ex: . +@prefix excode: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix skos: . +@prefix xsd: . + + + rdf:type owl:Ontology . + +excode:ExampleCodeList + rdf:type skos:ConceptScheme; + dct:isFormatOf ex:ExampleCodeList . + + + rdf:type ex:ExampleCodeList; + skos:inScheme excode:ExampleCodeList . + + + rdf:type ex:ExampleCodeList; + skos:inScheme excode:ExampleCodeList . diff --git a/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition.xml b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition.xml new file mode 100644 index 000000000..d55302243 --- /dev/null +++ b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition_runWithSCXML.xml b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition_runWithSCXML.xml new file mode 100644 index 000000000..8b7918842 --- /dev/null +++ b/shapechange-app/src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition_runWithSCXML.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shapechange-app/src/test/java/de/interactive_instruments/shapechange/app/OntologyTest.java b/shapechange-app/src/test/java/de/interactive_instruments/shapechange/app/OntologyTest.java index 4c3df59bb..819b8d792 100644 --- a/shapechange-app/src/test/java/de/interactive_instruments/shapechange/app/OntologyTest.java +++ b/shapechange-app/src/test/java/de/interactive_instruments/shapechange/app/OntologyTest.java @@ -146,4 +146,25 @@ public void testOwl_enumerationDatatypeProperty() { new String[] { "ttl" }, "testResults/owl/enumerationDatatypeProperty", "src/integrationtests/owl/enumerationDatatypeProperty/reference"); } + + @Test + public void testOwl_iso191502EnumerationAsDatatypeDefinition() { + + /* + * With target parameter iso191502EnumerationAsDatatypeDefinition, + * rule-owl-cls-iso191502Enumeration encodes an enumeration as the OWL 2 datatype + * definition DatatypeDefinition( DT DataOneOf( lt1 ... ltn ) ), which maps to + * the named DT being owl:equivalentClass to an anonymous rdfs:Datatype with the + * owl:oneOf list (W3C OWL 2 Mapping to RDF Graphs, Sec. 2.1, Table 1). + * + * The model is the one of testOwl_enumerationDatatypeProperty, which pins the + * default encoding, with owl:oneOf on the named datatype. The rest of the output + * is unchanged: Road.surface stays a data property whose qualified cardinality + * restriction uses owl:onDataRange with the named datatype. + */ + multiTest( + "src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/testEA_owl_iso191502EnumerationAsDatatypeDefinition.xml", + new String[] { "ttl" }, "testResults/owl/iso191502EnumerationAsDatatypeDefinition", + "src/integrationtests/owl/iso191502EnumerationAsDatatypeDefinition/reference"); + } } diff --git a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150.java b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150.java index 553d5fa20..d57725338 100644 --- a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150.java +++ b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150.java @@ -317,6 +317,17 @@ public class OWLISO19150 implements SingleTarget, MessageSource { public static final String PARAM_SUPPRESS_MESSAGES_FOR_UNSUPPORTED_CLASS_CATEGORY = "suppressMessagesForUnsupportedCategoryOfClasses"; + /** + * If set to true, {@value #RULE_OWL_CLS_ISO191502_ENUMERATION} + * encodes an enumeration as an OWL 2 datatype definition: the named + * rdfs:Datatype is owl:equivalentClass to an anonymous rdfs:Datatype that + * carries the owl:oneOf list of literals (W3C OWL 2 Structural Specification, + * Sec. 9.4; OWL 2 Mapping to RDF Graphs, Sec. 2.1, Table 1). By + * default, the owl:oneOf list is added directly to the named datatype, which + * the OWL 2 mapping to RDF does not read as an enumeration of literals. + */ + public static final String PARAM_ISO191502_ENUMERATION_AS_DATATYPE_DEFINITION = "iso191502EnumerationAsDatatypeDefinition"; + /** * key: a package, value: the according ontology object */ @@ -379,6 +390,7 @@ public class OWLISO19150 implements SingleTarget, MessageSource { private static String ontologyNameCode = null; private static String defaultTypeImplementation = null; private static boolean suppressMessagesForUnsupportedCategoryOfClasses = false; + private static boolean iso191502EnumerationAsDatatypeDefinition = false; private static String generalPropertyNamespaceAbbreviation = null; /** @@ -552,6 +564,9 @@ public void initialise(PackageInfo p, Model m, Options o, ShapeChangeResult r, b suppressMessagesForUnsupportedCategoryOfClasses = options.parameterAsBoolean(this.getClass().getName(), PARAM_SUPPRESS_MESSAGES_FOR_UNSUPPORTED_CLASS_CATEGORY, false); + iso191502EnumerationAsDatatypeDefinition = options.parameterAsBoolean(this.getClass().getName(), + PARAM_ISO191502_ENUMERATION_AS_DATATYPE_DEFINITION, false); + /* * Initialize an ontology for the package and - unless stated otherwise via a * rule - for the sub-packages in the same target namespace. Also create @@ -1273,6 +1288,7 @@ public void reset() { OWLISO19150.ontologyNameCode = null; OWLISO19150.defaultTypeImplementation = null; OWLISO19150.suppressMessagesForUnsupportedCategoryOfClasses = false; + OWLISO19150.iso191502EnumerationAsDatatypeDefinition = false; OWLISO19150.ontologyByPropertyConversionTargetReference = null; @@ -1372,6 +1388,15 @@ public boolean isSuppressMessagesForUnsupportedCategoryOfClasses() { return suppressMessagesForUnsupportedCategoryOfClasses; } + /** + * @return true if enumerations shall be encoded as datatype + * definitions, see + * {@value #PARAM_ISO191502_ENUMERATION_AS_DATATYPE_DEFINITION} + */ + public boolean isIso191502EnumerationAsDatatypeDefinition() { + return iso191502EnumerationAsDatatypeDefinition; + } + /** * @return the rdfNamespaceSeparator */ diff --git a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150ConfigurationValidator.java b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150ConfigurationValidator.java index 4af9dd315..9c2df4073 100644 --- a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150ConfigurationValidator.java +++ b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OWLISO19150ConfigurationValidator.java @@ -72,7 +72,8 @@ public class OWLISO19150ConfigurationValidator extends AbstractConfigurationVali .of(OWLISO19150.PARAM_CODE_LIST_OWL_CLASS_NAMESPACE, OWLISO19150.PARAM_CODE_LIST_OWL_CLASS_NAMESPACE_FOR_ENUMERATIONS, OWLISO19150.PARAM_CODE_NAMESPACE, OWLISO19150.PARAM_CODE_NAMESPACE_FOR_ENUMERATIONS, OWLISO19150.PARAM_DEFAULT_TYPE_IMPL, - OWLISO19150.PARAM_GENERAL_PROPERTY_NSABR, OWLISO19150.PARAM_LANGUAGE, + OWLISO19150.PARAM_GENERAL_PROPERTY_NSABR, + OWLISO19150.PARAM_ISO191502_ENUMERATION_AS_DATATYPE_DEFINITION, OWLISO19150.PARAM_LANGUAGE, OWLISO19150.PARAM_ONTOLOGYNAME_CODE_NAME, OWLISO19150.PARAM_ONTOLOGYNAME_TAGGED_VALUE_NAME, OWLISO19150.PARAM_OUTPUTFORMAT, OWLISO19150.PARAM_PROP_EXTERNAL_REFERENCE_TARGET_PROPERTY, OWLISO19150.PARAM_RDF_NAMESPACE_SEPARATOR, OWLISO19150.PARAM_RDFXMLWRITER_BLOCKRULES, diff --git a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OntologyModel.java b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OntologyModel.java index da646d282..7325e34d4 100644 --- a/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OntologyModel.java +++ b/shapechange-core/src/main/java/de/interactive_instruments/shapechange/core/target/ontology/OntologyModel.java @@ -3223,7 +3223,20 @@ public Resource addEnumeration(ClassInfo ci) { Literal en = ontmodel.createLiteral(pi.name()); enums.add(en); } - e.addProperty(OWL2.oneOf, ontmodel.createList(enums.iterator())); + + if (owliso19150.isIso191502EnumerationAsDatatypeDefinition()) { + /* + * DatatypeDefinition( DT DataOneOf( lt1 ... ltn ) ): the enumeration of + * literals is an anonymous data range, to which the named datatype is + * equivalent (W3C OWL 2 Mapping to RDF Graphs, Sec. 2.1, Table 1). + */ + Resource dataRange = ontmodel + .createResource(ontmodel.createResource(OWLISO19150.RDF_NS_W3C_RDFS + "Datatype")); + dataRange.addProperty(OWL2.oneOf, ontmodel.createList(enums.iterator())); + e.addProperty(OWL2.equivalentClass, dataRange); + } else { + e.addProperty(OWL2.oneOf, ontmodel.createList(enums.iterator())); + } } return e; }