From 4cc9d9ae0dd3dced485e78e424f93309126bc137 Mon Sep 17 00:00:00 2001 From: Todd Viegut Date: Wed, 16 Oct 2024 01:23:44 -0500 Subject: [PATCH] #171 --- .../langdale/profiles/ProfileSerializer.java | 160 ++++++++++++++++-- .../au/com/langdale/xmi/CIMInterpreter.java | 123 +++++++++++--- .../src/au/com/langdale/xmi/Translator.java | 120 ++----------- CIMUtil/src/au/com/langdale/xmi/UML.java | 18 ++ .../au/com/langdale/xmi/UMLInterpreter.java | 2 + .../src/au/com/langdale/xmi/XSDTypeUtils.java | 107 ++++++++++++ 6 files changed, 393 insertions(+), 137 deletions(-) create mode 100644 CIMUtil/src/au/com/langdale/xmi/XSDTypeUtils.java diff --git a/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java b/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java index b0a82789..edf3828a 100644 --- a/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java +++ b/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java @@ -7,10 +7,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Comparator; import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; +import java.util.TreeSet; import java.util.regex.Pattern; import javax.xml.transform.ErrorListener; @@ -40,7 +41,6 @@ import au.com.langdale.jena.TreeModelBase.Node; import au.com.langdale.kena.OntResource; import au.com.langdale.kena.ResIterator; -import au.com.langdale.kena.Resource; import au.com.langdale.profiles.ProfileClass.PropertyInfo; import au.com.langdale.profiles.ProfileModel.CatalogNode; import au.com.langdale.profiles.ProfileModel.EnvelopeNode; @@ -77,7 +77,20 @@ public class ProfileSerializer extends AbstractReader { private ArrayList templates = new ArrayList(); TransformerFactory factory = TransformerFactory.newInstance(); private final String xsd = XSD.anyURI.getNameSpace(); - private HashSet deferred = new HashSet(); + private TreeSet deferred = new TreeSet(new Comparator() { + @Override + public int compare(OntResource o1, OntResource o2) { + return o1.getURI().compareTo(o2.getURI()); + } + }); + private TreeSet primitives = new TreeSet(new Comparator() { + @Override + public int compare(OntResource o1, OntResource o2) { + String str1 = o1.getString(UML.primitiveDataType); + String str2 = o2.getString(UML.primitiveDataType); + return str1.compareTo(str2); + } + }); /** * Construct a serializer for the given MessageModel. @@ -352,8 +365,14 @@ private void emit(CatalogNode node) throws SAXException { Iterator it = deferred.iterator(); while (it.hasNext()) { - emit((OntResource) it.next()); + emitCIMDatatype((OntResource) it.next()); + } + + it = primitives.iterator(); + while (it.hasNext()) { + emitPrimitive((OntResource) it.next()); } + elem.close(); } @@ -412,13 +431,27 @@ private Element select(SubTypeNode node) throws SAXException { private void emit(ElementNode node) throws SAXException { Element elem; - if (node.isDatatype()) { OntResource range = node.getBaseProperty().getRange(); if (range == null) range = model.getOntModel().createResource(XSD.xstring.asNode()); - if (range.getNameSpace().equals(xsd)) { + if (range.hasProperty(UML.hasStereotype, UML.primitive) || range.getNameSpace().equals(xsd)) { elem = new Element("Simple"); + /** + * For backwards compatibility, rather than setting the "dataType" attribute + * to the <> type in the CIM (which is now specified as a property + * on the range which has historically specified the W3C XSD type such as: + * + * http://www.w3.org/2001/XMLSchema#float + * + * Given that we have numerous existing builders that produce profiles (e.g. + * XSD & JSON schemas) that are XSL transforms that would break if this were + * changed we have opted to go this route as an interim solution. When first + * class support for <> and <>s is introduced we will + * updated at that time to ensure this implementation is updated. + */ + elem.set("primitiveDataType", range.getString(UML.primitiveDataType)); + primitives.add(range); } else { elem = new Element("Domain"); elem.set("type", range.getLocalName()); @@ -516,6 +549,57 @@ private void emitInverse(ElementNode node, Element elem) throws SAXException { private void emit(ElementNode node, Element elem) throws SAXException { emit(node, elem, false); } + + private void emitValueUnitAndMultiplier(OntResource cimDatatype) throws SAXException { + if (cimDatatype == null) + return; + boolean hasMultiplier = cimDatatype.hasProperty(UML.hasMultiplier); + boolean hasUnits = cimDatatype.hasProperty(UML.hasUnits); + String valueDataType = cimDatatype.getString(UML.valueDataType, null); + String valuePrimitiveDataType = cimDatatype.getString(UML.valuePrimitiveDataType, null); + String unitDataType = cimDatatype.getString(UML.unitDataType, null); + String multiplierDataType = cimDatatype.getString(UML.multiplierDataType, null); + + Element valueElement = new Element("Value"); + valueElement.set("baseClass", valuePrimitiveDataType); + valueElement.set("type", valuePrimitiveDataType.substring(valuePrimitiveDataType.lastIndexOf("#") + 1)); + valueElement.set("xstype", valueDataType.substring(valueDataType.lastIndexOf("#") + 1)); + valueElement.set("name", "value"); + valueElement.set("constant", ""); + valueElement.set("ea_guid", cimDatatype.getString(UML.valueEAGUID, null)); + valueElement.set("hideInDiagrams", "false"); + valueElement.set("baseProperty", cimDatatype.getURI() + ".value"); + valueElement.set("basePropertyClass", cimDatatype.getURI()); + valueElement.set("minOccurs", ProfileModel.cardString(0)); + valueElement.set("maxOccurs", ProfileModel.cardString(1, "1")); + valueElement.close(); + + Element unitElement = new Element("Unit"); + unitElement.set("baseClass", unitDataType); + unitElement.set("type", unitDataType.substring(unitDataType.lastIndexOf("#") + 1)); + unitElement.set("name", "unit"); + unitElement.set("constant", (hasUnits ? cimDatatype.getString(UML.hasUnits, null) : "")); + unitElement.set("ea_guid", cimDatatype.getString(UML.unitEAGUID, null)); + unitElement.set("hideInDiagrams", "false"); + unitElement.set("baseProperty", cimDatatype.getURI() + ".unit"); + unitElement.set("basePropertyClass", cimDatatype.getURI()); + unitElement.set("minOccurs", ProfileModel.cardString(0)); + unitElement.set("maxOccurs", ProfileModel.cardString(1, "1")); + unitElement.close(); + + Element multiplierElement = new Element("Multiplier"); + multiplierElement.set("baseClass", multiplierDataType); + multiplierElement.set("type", multiplierDataType.substring(multiplierDataType.lastIndexOf("#") + 1)); + multiplierElement.set("name", "multiplier"); + multiplierElement.set("constant", (hasMultiplier ? cimDatatype.getString(UML.hasMultiplier, null) : "")); + multiplierElement.set("ea_guid", cimDatatype.getString(UML.multiplierEAGUID, null)); + multiplierElement.set("hideInDiagrams", "false"); + multiplierElement.set("baseProperty", cimDatatype.getURI() + ".multiplier"); + multiplierElement.set("basePropertyClass", cimDatatype.getURI()); + multiplierElement.set("minOccurs", ProfileModel.cardString(0)); + multiplierElement.set("maxOccurs", ProfileModel.cardString(1, "1")); + multiplierElement.close(); + } private void emit(ElementNode node, Element elem, boolean includeInverseBaseClass) throws SAXException { elem.set("name", node.getName()); @@ -640,9 +724,19 @@ private String xstype(OntResource type) { if (type.getNameSpace().equals(xsd)) return type.getLocalName(); - OntResource xtype = type.getEquivalentClass(); - if (xtype != null && xtype.getNameSpace().equals(xsd)) - return xtype.getLocalName(); + if (type.hasProperty(UML.hasStereotype, UML.primitive)) { + OntResource xsdType = type.getEquivalentClass(); + if (xsdType != null && xsdType.getNameSpace().equals(xsd)) + return xsdType.getLocalName(); + } else if (type.hasProperty(UML.hasStereotype, UML.cimdatatype)) { + OntResource cimPrimitive = type.getEquivalentClass(); + if (cimPrimitive != null) { + OntResource xsdType = cimPrimitive.getEquivalentClass(); + if (xsdType != null && xsdType.getNameSpace().equals(xsd)) { + return xsdType.getLocalName(); + } + } + } System.out.println("Warning: undefined datatype: " + type); return "string"; @@ -712,12 +806,15 @@ private void emit(MessageNode node) throws SAXException { elem.close(); } - private void emit(OntResource type) throws SAXException { + private void emitCIMDatatype(OntResource type) throws SAXException { Element elem = new Element("SimpleType"); + elem.set("dataType", type.getURI()); elem.set("name", type.getLocalName()); + if (type.hasProperty(UML.id)) elem.set("ea_guid", type.getString(UML.id)); + OntResource defin = type.getResource(RDFS.isDefinedBy); if (defin != null) { elem.set("package", defin.getLabel()); @@ -726,7 +823,50 @@ private void emit(OntResource type) throws SAXException { elem.set("xstype", xstype(type)); emitComment(type.getComment(null)); + emitValueUnitAndMultiplier(type); + elem.close(); + } + + /** + * Some history on the below change introduced in 2.3.0. The context is that for the + * CIMTool implementation of CIM <> UML classes (e.g. Decimal, Boolean, Date, + * Float, Date, others...) the perspective was that the intent of those classes is that + * they were representative of XSD schema type primitives and therefor the original + * CIMTool design was to essentially remove them from the CIMTool *.OWL profile + * representation generated by CIMTool. For a more detailed understanding of how this + * is done refer to the CIMInterpreter.applyPrimitiveStereotype() method. The result of + * the model processing done there is ultimately used here in the ProfileSerializer class. + * + * The below method was introduced in 2.3.0 as a result of the evolution in thinking in + * how we are now representing the profiles. Today, in formats such as RDFS2020 and others + * we include CIM <> as actual RDF classes (such as in the CGMES RDFS profiles). + * However, to preserve backwards compatibility we have chosen (for the moment) not to update + * the core representation in CIMTool but rather to limit changes to only the generated + * XML internal profile representation produced by the this ProfileSerializer class. Thus, + * in 2.3.0 we have added the additional method below to generated entries in the XML + * internal format to include "Primitive" entries that can be used by XSLT transform + * builders to generate output such as RDFS2020 and equivalent. To change the core internal + * format at this point would break CIMTool's current instance data validation features. + */ + private void emitPrimitive(OntResource type) throws SAXException { + Element elem = new Element("Primitive"); + + String dataType = type.getString(UML.primitiveDataType); + elem.set("dataType", dataType); + elem.set("name", dataType.substring(dataType.lastIndexOf("#") + 1)); + elem.set("hideInDiagrams", (isHidden(type) ? "true" : "false")); + + if (type.hasProperty(UML.id)) + elem.set("ea_guid", type.getString(UML.id)); + + OntResource defin = type.getResource(RDFS.isDefinedBy); + if (defin != null) { + elem.set("package", defin.getLabel()); + elem.set("packageURI", defin.getURI()); + } + elem.set("xstype", xstype(type)); + emitComment(type.getComment(null)); elem.close(); } diff --git a/CIMUtil/src/au/com/langdale/xmi/CIMInterpreter.java b/CIMUtil/src/au/com/langdale/xmi/CIMInterpreter.java index 8fa4db14..0961af99 100644 --- a/CIMUtil/src/au/com/langdale/xmi/CIMInterpreter.java +++ b/CIMUtil/src/au/com/langdale/xmi/CIMInterpreter.java @@ -4,21 +4,18 @@ */ package au.com.langdale.xmi; -import au.com.langdale.kena.OntModel; -import au.com.langdale.kena.OntResource; -import au.com.langdale.kena.ResIterator; -import au.com.langdale.kena.Resource; - -import java.util.TreeMap; - import com.hp.hpl.jena.graph.FrontsNode; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.vocabulary.OWL; -import com.hp.hpl.jena.vocabulary.OWL2; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.XSD; +import au.com.langdale.kena.OntModel; +import au.com.langdale.kena.OntResource; +import au.com.langdale.kena.ResIterator; +import au.com.langdale.kena.Resource; + /** * Extends the generic XMI interpreter to apply IEC CIM * modelling conventions. @@ -77,6 +74,7 @@ private OntModel postProcess(OntModel raw, String baseURI, boolean usePackageNam propagateComments(); applyStereotypes(); + classifyAttributes(); removeUntyped(); // convertToSubClassOf("extensionMSITE"); @@ -95,7 +93,7 @@ private OntModel postProcess(OntModel raw, String baseURI, boolean usePackageNam OntResource aParentPackage = aClass.getIsDefinedBy(); System.out.println(String.format("4 - Class: %s; Package: %s", name, (aParentPackage != null ? aParentPackage.getLabel() : aParentPackage))); }); */ - + return getModel(); } @@ -185,10 +183,17 @@ private void applyPrimitiveStereotype(Resource stereo, boolean interpret_value) private void applyPrimitiveStereotype(OntResource clss, boolean interpret_value) { OntResource truetype = null; - String units = null; + String unit = null; + String valueDataType = null; + String valuePrimitiveDataType = null; + String valueEAGUID = null; + String unitDataType = null; + String unitEAGUID = null; String multiplier = null; + String multiplierDataType = null; + String multiplierEAGUID = null; - // strip the classes properties, record the value and units information + // strip the classes properties, record the value, units, and multiplier information ResIterator it = model.listSubjectsBuffered(RDFS.domain, clss); while(it.hasNext()) { OntResource m = it.nextResource(); @@ -197,12 +202,22 @@ private void applyPrimitiveStereotype(OntResource clss, boolean interpret_value) String name = m.getLabel(); if(name != null) { // this is a CIM-style annotation to indicate the primitive datatype - if( name.equals("value")) + if( name.equals("value")) { truetype = m.getResource(RDFS.range); - if( name.equals("unit") || name.equals("units")) - units = m.getString(UML.hasInitialValue); - if( name.equals("multiplier")) + valueDataType = m.getRange().getURI(); + valuePrimitiveDataType = m.getRange().getString(UML.primitiveDataType); + valueEAGUID = m.getString(UML.id); + } + if( name.equals("unit") || name.equals("units")) { + unit = m.getString(UML.hasInitialValue); + unitDataType = m.getRange().getURI(); + unitEAGUID = m.getString(UML.id); + } + if( name.equals("multiplier")) { multiplier = m.getString(UML.hasInitialValue); + multiplierDataType = m.getRange().getURI(); + multiplierEAGUID = m.getString(UML.id); + } } } // remove spurious property attached to datatype @@ -210,21 +225,87 @@ private void applyPrimitiveStereotype(OntResource clss, boolean interpret_value) } // for XML Schema datatypes remove all definitions - if( clss.getNameSpace().equals(XSD.getURI())){ - clss.removeProperties(); + if(clss.hasProperty(UML.hasStereotype, UML.primitive) || clss.getNameSpace().equals(XSD.getURI())){ + /** + * Some history on the below change introduced in 2.3.0. The context for the original + * implementation in 2010/2011 was that the implementation of CIM <> UML + * classes (e.g. Decimal, Integer, Boolean, Date, Float, DateTime, Month, etc.) + * was originally from the perspective that the intent in the UML was that the are + * representative of XSD schema types. Based on that assumption, the implementation + * resulted in the removing them as RDF Classes from the *.OWL profiles generated + * by CIMTool. This occurs via a two step process during the import of a CIM schema + * (i.e. an *.xmi, *.eap, *.qea, etc. file) into CIMTool. During the execution of the + * CIMInterpreter.postProcess() method the Translator class runs two passes as part + * if its translation processing. It is during "pass 2" that it does a rename in the + * Translator.renameResource(OntResource r, String l) method and renames/transforms the + * CIM <> class itself to it's XSD schema type. That translation leaves the + * OntResource looking like the following if you were to perform a OntResource.desribe(): + * + * http://www.w3.org/2001/XMLSchema#boolean + * http://langdale.com.au/2005/UML#hasStereotype = http://langdale.com.au/2005/UML#primitive + * http://www.w3.org/2000/01/rdf-schema#isDefinedBy = http://iec.ch/TC57/CIM100#Package_Domain + * http://langdale.com.au/2005/UML#id = "EAID_9F8964F1_6C32_465b_A83D_F5A201A291C3" + * http://www.w3.org/2000/01/rdf-schema#label = "Boolean" + * http://www.w3.org/2000/01/rdf-schema#comment = "A type with the value space "true" and "false"." + * + * Thus, the translation step was a preparatory step that essentially was completed in + * this method where the call to removeProperites(), commented out below, would remove + * the additional properties above. + * + * However, as things have evolved in the broader CIM community and as RDFS2020 and other + * representations have been introduced, those representations explicitly represent the + * CIM <> classes as RDF Classes in those newer formats (such as in the CGMES + * RDFS profiles). However, to preserve backwards compatibility we have chosen (for now) + * not to update the core representation but rather to limit changes to only the generated + * XML internal profile representation produced by the ProfileSerializer class. Thus by + * changing the below to remove only the RDF.type properties it filters it still "filters + * out" the <> classes from the core *.OWL profile (which only includes valid + * OntResources defined with a property of: + * + * http://www.w3.org/1999/02/22-rdf-syntax-ns#type = http://www.w3.org/2002/07/owl#Class + * + * Thus, the change below preserves the remaining 4 properties (for package name, comments, + * stereotypes, etc.) that we can use "downstream" when generating the XML internal profile + * used by the XSLT RDFS2020 builder (and others). for further details, refer to the + * ProfileSerializer.emitPrimitive() method on that class. + */ + //clss.removeProperties(); + clss.removeAll(RDF.type); + // + String localName = clss.getLocalName(); + FrontsNode xsdType = XSDTypeUtils.selectXSDType(localName); + if (xsdType != null) { + clss.addProperty(OWL.equivalentClass, xsdType); + } } // for defined datatypes, establish an RDFS datatype else { clss.removeAll(RDF.type); clss.addProperty(RDF.type, RDFS.Datatype); - - if( truetype != null && ! clss.equals(truetype)) + // + if( truetype != null && !clss.equals(truetype) ) clss.addProperty( OWL.equivalentClass, truetype ); - if( units != null ) - clss.addProperty( UML.hasUnits, units); + if( valueDataType != null ) + clss.addProperty( UML.valueDataType, valueDataType); + if ( valuePrimitiveDataType != null) + clss.addProperty( UML.valuePrimitiveDataType, valuePrimitiveDataType); + if( valueEAGUID != null ) + clss.addProperty( UML.valueEAGUID, valueEAGUID); + // + if( unit != null ) + clss.addProperty( UML.hasUnits, unit); + if( unitDataType != null ) + clss.addProperty( UML.unitDataType, unitDataType); + if( unitEAGUID != null ) + clss.addProperty( UML.unitEAGUID, unitEAGUID); + // if( multiplier != null ) clss.addProperty( UML.hasMultiplier, multiplier); + if( multiplierDataType != null ) + clss.addProperty( UML.multiplierDataType, multiplierDataType); + if( multiplierEAGUID != null ) + clss.addProperty( UML.multiplierEAGUID, multiplierEAGUID); } } diff --git a/CIMUtil/src/au/com/langdale/xmi/Translator.java b/CIMUtil/src/au/com/langdale/xmi/Translator.java index 63eea022..ffc67997 100644 --- a/CIMUtil/src/au/com/langdale/xmi/Translator.java +++ b/CIMUtil/src/au/com/langdale/xmi/Translator.java @@ -14,7 +14,6 @@ import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; -import com.hp.hpl.jena.vocabulary.XSD; import au.com.langdale.kena.ModelFactory; import au.com.langdale.kena.OntModel; @@ -152,13 +151,9 @@ protected FrontsNode renameResource(OntResource r, String l) { OntResource stereotype = result.createResource(UML.NS + l.toLowerCase()); stereotype.addLabel(l, null); return stereotype; - } - - else if (r.hasProperty(RDF.type, OWL.AnnotationProperty)) { + } else if (r.hasProperty(RDF.type, OWL.AnnotationProperty)) { return annotationResource(l); - } - - else if (r.hasProperty(RDF.type, UML.Package)) { + } else if (r.hasProperty(RDF.type, UML.Package)) { // there are three strategies evolved over time to assign package URI's if (uniqueNamespaces) { if (extraDecoration) @@ -246,12 +241,16 @@ protected FrontsNode renameResource(OntResource r, String l) { if (r.hasProperty(RDF.type, OWL.Class)) { if ((r.hasProperty(UML.hasStereotype, UML.datatype) || r.hasProperty(UML.hasStereotype, UML.cimdatatype) || r.hasProperty(UML.hasStereotype, UML.primitive)) - && !r.hasProperty(UML.hasStereotype, UML.enumeration)) { - FrontsNode x = selectXSDType(l); - if (x != null) - return x; + && !r.hasProperty(UML.hasStereotype, UML.enumeration)) { + FrontsNode x = XSDTypeUtils.selectXSDType(l); + if (x != null) { + OntResource resource = result.createResource(x.toString()); + resource.addProperty(UML.primitiveDataType, namespace + l); + return resource; + } } - return result.createResource(namespace + l); + OntResource resource = result.createResource(namespace + l); + return resource; } if (r.hasProperty(RDF.type, OWL.ObjectProperty) || r.hasProperty(RDF.type, OWL.DatatypeProperty) @@ -281,9 +280,10 @@ protected FrontsNode renameResource(OntResource r, String l) { // System.out.println("Unrecognised UML element name: " + l + " uri: " + // r.getURI()); // this is almost certainly a top level datatype declaration - FrontsNode x = selectXSDType(l); - if (x != null) + FrontsNode x = XSDTypeUtils.selectXSDType(l); + if (x != null) { return x; + } return result.createResource(namespace + l); } @@ -303,98 +303,6 @@ protected void add(FrontsNode s, FrontsNode p, Node o) { } } - /** - * Select XSD datatypes for UML attributes. - * - * @param l A simple name for the datatype received from the UML. - * @return A resource representing one of the XSD datatypes recommended for OWL. - */ - protected FrontsNode selectXSDType(String l) { - // TODO: add more XSD datatypes here - if (l.equalsIgnoreCase("integer")) - return XSD.integer; - else if (l.equalsIgnoreCase("int")) - return XSD.xint; - else if (l.equalsIgnoreCase("unsigned")) - return XSD.unsignedInt; - else if (l.equalsIgnoreCase("ulong") || l.equalsIgnoreCase("ulonglong")) - return XSD.unsignedLong; - else if (l.equalsIgnoreCase("short")) - return XSD.xshort; - else if (l.equalsIgnoreCase("long") || l.equalsIgnoreCase("longlong")) - return XSD.xlong; - else if (l.equalsIgnoreCase("string") || l.equalsIgnoreCase("char")) - return XSD.xstring; - else if (l.equalsIgnoreCase("float")) - return XSD.xfloat; - else if (l.equalsIgnoreCase("double") || l.equalsIgnoreCase("longdouble")) - return XSD.xdouble; - else if (l.equalsIgnoreCase("boolean") || l.equalsIgnoreCase("bool")) - return XSD.xboolean; - else if (l.equalsIgnoreCase("decimal")) - return XSD.decimal; - else if (l.equalsIgnoreCase("nonNegativeInteger")) - return XSD.nonNegativeInteger; - else if (l.equalsIgnoreCase("date")) - return XSD.date; - else if (l.equalsIgnoreCase("time")) - return XSD.time; - else if (l.equalsIgnoreCase("datetime")) - return XSD.dateTime; - else if (l.equalsIgnoreCase("absolutedatetime")) - return XSD.dateTime; - else if (l.equalsIgnoreCase("duration")) - return XSD.duration; - else if (l.equalsIgnoreCase("monthday")) - return XSD.gMonthDay; - /** - * Below reflects the introduction of the URI primitive domain type in CIM18. - */ - else if (l.equalsIgnoreCase("uri")) - return XSD.anyURI; - /** - * Below reflects the introduction of the UUID primitive domain type in CIM18. - * - * We are using XSD.xstring and not XSD:ID datatype here. This is due to the - * fact that xsd:ID in XML Schema Definition (XSD) is not suitable for - * representing a UUID. Here's why: - * - * Purpose and Constraints: - * - * - xsd:ID is intended to represent a unique identifier within an XML document. - * It must be unique within the document and is generally used for linking with - * xsd:IDREF. - * - * - xsd:ID must follow the rules for XML names, meaning it must start with a - * letter or underscore and cannot contain certain characters, such as hyphens - * (-) or numbers at the beginning. This makes it incompatible with the typical - * structure of UUIDs, which are typically in the form 8-4-4-4-12 hexadecimal - * digits separated by hyphens. - * - * UUID Format: - * - * - A UUID (Universally Unique Identifier) is typically represented as a - * 36-character string, including 32 hexadecimal digits and 4 hyphens. This - * format does not conform to the XML name rules required by xsd:ID. - * - * Alternative: - * - * - Instead of using xsd:ID, we need to use xsd:string with a pattern (i.e. - * facet) or define a custom type with a pattern that matches the UUID format - * such as in this example: - * - * - * - * - * - */ - else if (l.equalsIgnoreCase("uuid")) - return XSD.xstring; - else - return null; - } - /** * Convert a string to an NCNAME by substituting underscores for invalid * characters. diff --git a/CIMUtil/src/au/com/langdale/xmi/UML.java b/CIMUtil/src/au/com/langdale/xmi/UML.java index a8710559..28eded9d 100644 --- a/CIMUtil/src/au/com/langdale/xmi/UML.java +++ b/CIMUtil/src/au/com/langdale/xmi/UML.java @@ -94,8 +94,19 @@ public class UML { public final static Property hasInitialValue = ResourceFactory.createProperty(NS + "hasInitialValue"); // units and multiplier are not really a UML concepts, we infer these under CIM conventions + // these properties are included in order to allow for "flattened" CIMDatatypes meaning that + // the .OWL profile will not contain representations of these CIM classes that contain the + // value/unit/multiplier attributes. public final static Property hasUnits = ResourceFactory.createProperty(NS + "hasUnits"); public final static Property hasMultiplier = ResourceFactory.createProperty(NS + "hasMultiplier"); + public final static Property valueDataType = ResourceFactory.createProperty(NS + "valueDataType"); + public final static Property valuePrimitiveDataType = ResourceFactory.createProperty(NS + "valuePrimitiveDataType"); + public final static Property valueEAGUID = ResourceFactory.createProperty(NS + "valueEAGUID"); + public final static Property unitDataType = ResourceFactory.createProperty(NS + "unitDataType"); + public final static Property unitEAGUID = ResourceFactory.createProperty(NS + "unitEAGUID"); + public final static Property multiplierDataType = ResourceFactory.createProperty(NS + "multiplierDataType"); + public final static Property multiplierEAGUID = ResourceFactory.createProperty(NS + "multiplierEAGUID"); + public final static Property primitiveDataType = ResourceFactory.createProperty(NS + "primitiveDataType"); // tags we recognise that aid conversion to RDFS/OWL public final static Property baseuri = ResourceFactory.createProperty(NS + "baseuri"); @@ -162,6 +173,13 @@ public static void loadOntology( OntModel model ) { model.createAnnotationProperty(hasInitialValue.getURI()); model.createAnnotationProperty(hasUnits.getURI()); model.createAnnotationProperty(hasMultiplier.getURI()); + model.createAnnotationProperty(valueDataType.getURI()); + model.createAnnotationProperty(valueEAGUID.getURI()); + model.createAnnotationProperty(unitDataType.getURI()); + model.createAnnotationProperty(unitEAGUID.getURI()); + model.createAnnotationProperty(multiplierDataType.getURI()); + model.createAnnotationProperty(multiplierEAGUID.getURI()); + model.createAnnotationProperty(primitiveDataType.getURI()); model.createAnnotationProperty(id.getURI()); model.createAnnotationProperty(hasMaxCardinality.getURI()); model.createAnnotationProperty(hasMinCardinality.getURI()); diff --git a/CIMUtil/src/au/com/langdale/xmi/UMLInterpreter.java b/CIMUtil/src/au/com/langdale/xmi/UMLInterpreter.java index 048e136f..05a11c08 100644 --- a/CIMUtil/src/au/com/langdale/xmi/UMLInterpreter.java +++ b/CIMUtil/src/au/com/langdale/xmi/UMLInterpreter.java @@ -116,6 +116,8 @@ else if( ! type.hasRDFType()) { else attrib.addRDFType( OWL.ObjectProperty); attrib.addRDFType(OWL.FunctionalProperty); + } else { + System.err.println(attrib.getURI() + ": has no range. It's domain is: " + attrib.getDomain()); } } } diff --git a/CIMUtil/src/au/com/langdale/xmi/XSDTypeUtils.java b/CIMUtil/src/au/com/langdale/xmi/XSDTypeUtils.java new file mode 100644 index 00000000..1edd4eaa --- /dev/null +++ b/CIMUtil/src/au/com/langdale/xmi/XSDTypeUtils.java @@ -0,0 +1,107 @@ +/** + * + */ +package au.com.langdale.xmi; + +import com.hp.hpl.jena.graph.FrontsNode; +import com.hp.hpl.jena.vocabulary.XSD; + +/** + * + */ +public final class XSDTypeUtils { + + + /** + * Select XSD datatypes for UML attributes. + * + * @param l A simple name for the datatype received from the UML. + * @return A resource representing one of the XSD datatypes recommended for OWL. + */ + public static FrontsNode selectXSDType(String l) { + // TODO: add more XSD datatypes here + if (l.equalsIgnoreCase("integer")) + return XSD.integer; + else if (l.equalsIgnoreCase("int")) + return XSD.xint; + else if (l.equalsIgnoreCase("unsigned")) + return XSD.unsignedInt; + else if (l.equalsIgnoreCase("ulong") || l.equalsIgnoreCase("ulonglong")) + return XSD.unsignedLong; + else if (l.equalsIgnoreCase("short")) + return XSD.xshort; + else if (l.equalsIgnoreCase("long") || l.equalsIgnoreCase("longlong")) + return XSD.xlong; + else if (l.equalsIgnoreCase("string") || l.equalsIgnoreCase("char")) + return XSD.xstring; + else if (l.equalsIgnoreCase("float")) + return XSD.xfloat; + else if (l.equalsIgnoreCase("double") || l.equalsIgnoreCase("longdouble")) + return XSD.xdouble; + else if (l.equalsIgnoreCase("boolean") || l.equalsIgnoreCase("bool")) + return XSD.xboolean; + else if (l.equalsIgnoreCase("decimal")) + return XSD.decimal; + else if (l.equalsIgnoreCase("nonNegativeInteger")) + return XSD.nonNegativeInteger; + else if (l.equalsIgnoreCase("date")) + return XSD.date; + else if (l.equalsIgnoreCase("time")) + return XSD.time; + else if (l.equalsIgnoreCase("datetime")) + return XSD.dateTime; + else if (l.equalsIgnoreCase("absolutedatetime")) + return XSD.dateTime; + else if (l.equalsIgnoreCase("duration")) + return XSD.duration; + else if (l.equalsIgnoreCase("monthday")) + return XSD.gMonthDay; + /** + * Below reflects the introduction of the URI primitive domain type in CIM18. + */ + else if (l.equalsIgnoreCase("uri")) + return XSD.anyURI; + /** + * Below reflects the introduction of the UUID primitive domain type in CIM18. + * + * We are using XSD.xstring and not XSD:ID datatype here. This is due to the + * fact that xsd:ID in XML Schema Definition (XSD) is not suitable for + * representing a UUID. Here's why: + * + * Purpose and Constraints: + * + * - xsd:ID is intended to represent a unique identifier within an XML document. + * It must be unique within the document and is generally used for linking with + * xsd:IDREF. + * + * - xsd:ID must follow the rules for XML names, meaning it must start with a + * letter or underscore and cannot contain certain characters, such as hyphens + * (-) or numbers at the beginning. This makes it incompatible with the typical + * structure of UUIDs, which are typically in the form 8-4-4-4-12 hexadecimal + * digits separated by hyphens. + * + * UUID Format: + * + * - A UUID (Universally Unique Identifier) is typically represented as a + * 36-character string, including 32 hexadecimal digits and 4 hyphens. This + * format does not conform to the XML name rules required by xsd:ID. + * + * Alternative: + * + * - Instead of using xsd:ID, we need to use xsd:string with a pattern (i.e. + * facet) or define a custom type with a pattern that matches the UUID format + * such as in this example: + * + * + * + * + * + */ + else if (l.equalsIgnoreCase("uuid")) + return XSD.xstring; + else + return null; + } + +}