diff --git a/docs/SimpleEmployerExample.png b/docs/SimpleEmployerExample.png index 4ba5f77..edd7c37 100644 Binary files a/docs/SimpleEmployerExample.png and b/docs/SimpleEmployerExample.png differ diff --git a/docs/uml2semantics.qea b/docs/uml2semantics.qea index 376f1ce..fa5c6a7 100644 Binary files a/docs/uml2semantics.qea and b/docs/uml2semantics.qea differ diff --git a/src/main/scala/org/uml2semantics/model/UMLClassDiagram.scala b/src/main/scala/org/uml2semantics/model/UMLClassDiagram.scala index 081d4fd..3f56fb3 100644 --- a/src/main/scala/org/uml2semantics/model/UMLClassDiagram.scala +++ b/src/main/scala/org/uml2semantics/model/UMLClassDiagram.scala @@ -554,12 +554,9 @@ case class UMLClass(classIdentity: UMLClassIdentity, case class UMLClassAttributeDefinition(definition: String = "") sealed trait UMLClassAttributeType - case class UMLXMLDataType(attributeType: SupportedDataType) extends UMLClassAttributeType - case class UMLClassIdentityType(attributeType: UMLClassIdentity) extends UMLClassAttributeType - - +case class UMLEnumerationIdentityType(attributeType: UMLEnumerationIdentity) extends UMLClassAttributeType case class CurieBasedUMLClassAttributeType(attributeType: Curie) extends UMLClassAttributeType //case class UndefinedUMLClassAttributeType() extends UMLClassAttributeType @@ -571,20 +568,25 @@ object UMLClassAttributeType: def apply(s: String): UMLClassAttributeType = logger.debug(s"s=$s ${Code.source}") - require(UMLClassIdentity.findClassNamedElement(s).nonEmpty || SupportedDataType.unapply(s).nonEmpty - || Curie.isCurieBasedOnConfiguredPrefix(s) || s.isEmpty, - s"""A class attribute must have a type that is either a class, that has been specified, + require(UMLClassIdentity.findClassNamedElement(s).nonEmpty || + UMLEnumerationIdentity.findEnumerationNamedElement(s).nonEmpty || + SupportedDataType.unapply(s).nonEmpty || + Curie.isCurieBasedOnConfiguredPrefix(s) || + s.isEmpty, + s"""A class attribute must have a type that is either a class or enumeration, that has been specified, or an XML data type or an curie based on a known prefix. Prefixes are specified using the -x option when running uml2semantics. - "$s" is not recognised as either a class or an XML data type.""") + "$s" is not recognised as either a class or an enumeration or an XML data type.""") UMLClassIdentity.findClassNamedElement(s) match case Some(classNamedElement) => UMLClassIdentityType(classNamedElement) - case None => SupportedDataType.unapply(s) match - case Some(x) => UMLXMLDataType(x) - case None => - val curieOption: Option[Curie] = Curie.unapply(s) - curieOption match - case Some(curie) => CurieBasedUMLClassAttributeType(curie) - case None => UndefinedUMLClassAttributeType + case None => UMLEnumerationIdentity.findEnumerationNamedElement(s) match + case Some(enumerationNamedElement) => UMLEnumerationIdentityType(enumerationNamedElement) + case None => SupportedDataType.unapply(s) match + case Some(x) => UMLXMLDataType(x) + case None => + val curieOption: Option[Curie] = Curie.unapply(s) + curieOption match + case Some(curie) => CurieBasedUMLClassAttributeType(curie) + case None => UndefinedUMLClassAttributeType def unapply(s: String): Option[UMLClassAttributeType] = logger.debug(s"s=$s ${Code.source}") diff --git a/src/main/scala/org/uml2semantics/owl/UML2OWLWriter.scala b/src/main/scala/org/uml2semantics/owl/UML2OWLWriter.scala index 53d2823..cf86123 100644 --- a/src/main/scala/org/uml2semantics/owl/UML2OWLWriter.scala +++ b/src/main/scala/org/uml2semantics/owl/UML2OWLWriter.scala @@ -87,6 +87,8 @@ class UML2OWLWriter(val umlClassDiagram: UMLClassDiagram): owlProperty = createOWLDataProperty(umlClassAttribute, errorMessages, domain, attributeType) case UMLClassIdentityType(attributeType) => owlProperty = createOWLObjectProperty(umlClassAttribute, errorMessages, domain, attributeType) + case UMLEnumerationIdentityType(attributeType) => + owlProperty = createOWLObjectProperty(umlClassAttribute, errorMessages, domain, attributeType) case CurieBasedUMLClassAttributeType(attributeType) => owlProperty = createOWLObjectPropertyBasedOnConfiguredPrefix(umlClassAttribute, errorMessages, domain, attributeType) case UndefinedUMLClassAttributeType => @@ -157,6 +159,56 @@ class UML2OWLWriter(val umlClassDiagram: UMLClassDiagram): case UMLMultiplicity(UMLInfiniteCardinality(_), UMLInfiniteCardinality(_)) => objectProperty + private def createOWLObjectProperty(umlClassAttribute: UMLClassAttribute, + errorMessages: mutable.Seq[String], + domain: OWLClass, + umlEnumerationIdentity: UMLEnumerationIdentity): OWLObjectProperty = + logger.debug(s"createOWLObjectProperty: umlClassAttribute=$umlClassAttribute, errorMessages = $errorMessages, " + + s"domain=$domain, umlEnumerationIdentity=$umlEnumerationIdentity ${Code.source}") + val objectProperty = dataFactory.getOWLObjectProperty(umlClassAttribute.attributeIdentity.attributeIRI.iri) + val domainChangeApplied = manager.addAxiom(ontology, dataFactory.getOWLObjectPropertyDomainAxiom(objectProperty, domain)) + if domainChangeApplied != SUCCESSFULLY then + errorMessages :+ s"Domain axiom for object property ${objectProperty.getIRI} could not be added for " + + s"${umlClassAttribute.attributeIdentity.attributeIRI.iri}" + val rangeChangeApplied = manager.addAxiom(ontology, dataFactory.getOWLObjectPropertyRangeAxiom(objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri))) + if rangeChangeApplied != SUCCESSFULLY then + errorMessages :+ s"Range axiom for data property ${objectProperty.getIRI} could not be added for " + + s"${umlClassAttribute.attributeIdentity.attributeIRI.iri}" + umlClassAttribute.multiplicity match + case UMLMultiplicity(UMLNonNegativeCardinality(minCardinality), UMLNonNegativeCardinality(maxCardinality)) => + logger.trace(s"minCardinality=$minCardinality ${Code.source}") + val axiom = + if minCardinality == maxCardinality && minCardinality != 0 then + dataFactory.getOWLSubClassOfAxiom(domain, dataFactory.getOWLObjectExactCardinality(maxCardinality, objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri))) + else if minCardinality > 0 then + dataFactory.getOWLSubClassOfAxiom(domain, + dataFactory.getOWLObjectIntersectionOf( + dataFactory.getOWLObjectMinCardinality(minCardinality, objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri)), + dataFactory.getOWLObjectMaxCardinality(maxCardinality, objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri)))) + else + dataFactory.getOWLSubClassOfAxiom(domain, dataFactory.getOWLObjectMaxCardinality(maxCardinality, objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri))) + if manager.addAxiom(ontology, axiom) != SUCCESSFULLY then + errorMessages :+ s"Could not add subclass axiom representing cardinalities [$minCardinality, $maxCardinality] for " + + s"${umlClassAttribute.attributeIdentity.attributeLabel}" + case UMLMultiplicity(UMLNonNegativeCardinality(minCardinality), UMLInfiniteCardinality(_)) => + if minCardinality > 0 then + val axiom = dataFactory.getOWLSubClassOfAxiom(domain, + dataFactory.getOWLObjectMinCardinality(minCardinality, objectProperty, + dataFactory.getOWLClass(umlEnumerationIdentity.enumerationIRI.iri))) + if manager.addAxiom(ontology, axiom) != SUCCESSFULLY then + errorMessages :+ s"Could not add subclass axiom representing cardinalities for [$minCardinality, *]" + + s"${umlClassAttribute.attributeIdentity.attributeLabel}" + case UMLMultiplicity(UMLInfiniteCardinality(_), UMLNonNegativeCardinality(maxCardinality)) => + errorMessages :+ s"Multiplicity error found with multiplicity = [*, $maxCardinality] for attribute = " + + s"${umlClassAttribute.attributeIdentity.attributeLabel}." + case UMLMultiplicity(UMLInfiniteCardinality(_), UMLInfiniteCardinality(_)) => + objectProperty + private def createOWLObjectProperty(umlClassAttribute: UMLClassAttribute, errorMessages: mutable.Seq[String], domain: OWLClass): OWLObjectProperty = diff --git a/src/main/scala/org/uml2semantics/reader/TSVReader.scala b/src/main/scala/org/uml2semantics/reader/TSVReader.scala index d040b4e..dfc9227 100644 --- a/src/main/scala/org/uml2semantics/reader/TSVReader.scala +++ b/src/main/scala/org/uml2semantics/reader/TSVReader.scala @@ -79,8 +79,9 @@ def parseAttributes(maybeTsvFile: Option[File], ontologyPrefix: PrefixNamespace) logger.trace(s"m = $m") val classNamedElement = UMLClassIdentity.findClassNamedElement(m(ClassAttributesHeader.ClassName.toString)) + val enumerationNamedElement = UMLEnumerationIdentity.findEnumerationNamedElement(m(ClassAttributesHeader.ClassName.toString)) logger.trace(s"classNamedElement = $classNamedElement") - if classNamedElement.isDefined then + if classNamedElement.isDefined || enumerationNamedElement.isDefined then logger.trace(s"mClassOrPrimitiveType.toString = {${m(ClassOrPrimitiveType.toString)}}") val curieOption: Option[Curie] = if m(Curie.toString).contains(":") then Some(org.uml2semantics.model.Curie(m(Curie.toString))) @@ -191,14 +192,19 @@ def parseEnumerationValues(maybeTsvFile: Option[File], ontologyPrefix: PrefixNam end parseEnumerationValues def parseUMLClassDiagram(input: InputParameters): UMLClassDiagram = + var umlClasses = parseClasses(input.classesTsv, PrefixNamespace(input.ontologyPrefix)) + var umlEnumerations = parseEnumerations(input.enumerationsTsv, PrefixNamespace(input.ontologyPrefix)) + var umlAttributes = parseAttributes(input.attributesTsv, PrefixNamespace(input.ontologyPrefix)) + var umlEnumerationValues = parseEnumerationValues(input.enumerationValuesTsv, PrefixNamespace(input.ontologyPrefix)) + UMLClassDiagram( input.owlOntologyFile.get, OntologyIRI(input.ontologyIRI), PrefixNamespace(input.ontologyPrefix), - parseClasses(input.classesTsv, PrefixNamespace(input.ontologyPrefix)), - parseAttributes(input.attributesTsv, PrefixNamespace(input.ontologyPrefix)), - parseEnumerations(input.enumerationsTsv, PrefixNamespace(input.ontologyPrefix)), - parseEnumerationValues(input.enumerationValuesTsv, PrefixNamespace(input.ontologyPrefix))) + umlClasses, + umlAttributes, + umlEnumerations, + umlEnumerationValues) diff --git a/src/test/resources/Employer - Attributes.tsv b/src/test/resources/Employer - Attributes.tsv index 40c6166..5e71708 100644 --- a/src/test/resources/Employer - Attributes.tsv +++ b/src/test/resources/Employer - Attributes.tsv @@ -3,4 +3,7 @@ Person name xsd:string Person surname xsd:string Person dateOfBirth xsd:dateTime Employee employedBy Employer 1 1 -Employer employes Employee 1 * \ No newline at end of file +Employee renumeration Renumeration 1 1 +Employer employes Employee 1 * +Renumeration rate xsd:decimal 1 1 +Renumeration paymentSchedule PaymentSchedule 1 1 \ No newline at end of file diff --git a/src/test/resources/Employer - Classes.tsv b/src/test/resources/Employer - Classes.tsv index bc27587..ee08db7 100644 --- a/src/test/resources/Employer - Classes.tsv +++ b/src/test/resources/Employer - Classes.tsv @@ -1,4 +1,5 @@ Curie Name Definition ParentNames Person Employee Person - Employer Person \ No newline at end of file + Employer Person + Renumeration \ No newline at end of file diff --git a/src/test/resources/employer.rdf b/src/test/resources/employer.rdf index d566dd5..aa7665b 100644 --- a/src/test/resources/employer.rdf +++ b/src/test/resources/employer.rdf @@ -33,6 +33,17 @@ + + + + + + + renumeration + + + + @@ -44,6 +55,17 @@ + + + + + + + paymentSchedule + + + + + + + + + + rate + + + + + + + + + + + + 1 + + + + + + + 1 + + + + Renumeration + + + +