diff --git a/CIMToolPlugin/src/au/com/langdale/cimtoole/editors/profile/Populate.java b/CIMToolPlugin/src/au/com/langdale/cimtoole/editors/profile/Populate.java index 68ed549c..90aa58ac 100644 --- a/CIMToolPlugin/src/au/com/langdale/cimtoole/editors/profile/Populate.java +++ b/CIMToolPlugin/src/au/com/langdale/cimtoole/editors/profile/Populate.java @@ -106,6 +106,11 @@ protected Template define() { Group( TreeViewer("left", true), TreeViewer("right", true)), + Group( + Row( + Span(ViewCheckBox("concrete", "Set selected classes to concrete when added to the profile")), + Span(ViewCheckBox("required", "Set selected attributes to required when added to the profile")) + )), Group( Right( Row( @@ -168,27 +173,32 @@ public void selectionChanged(SelectionChangedEvent event) { private Picker allLeft = new Picker() { protected void handle(Node node) { + boolean arePropertiesRequired = getButton("required").getSelection(); + boolean isConcrete = getButton("concrete").getSelection(); SortedNode target = getProfileNode(); Collection args = target.profileExpandArgs(node); if(args.size() < 50 || confirm(node.toString(), args.size())) { - target.profileAddAllDeep(args); + target.profileAddAllDeep(args, isConcrete, arePropertiesRequired); } } }; private Picker anonLeft = new Picker() { protected void handle(Node node) { + boolean arePropertiesRequired = getButton("required").getSelection(); SortedNode target = getProfileNode(); - target.profileAddAnon(node); + target.profileAddAnon(node, false, arePropertiesRequired); } }; private Picker toLeft = new Picker() { protected void handle(Node node) { + boolean arePropertiesRequired = getButton("required").getSelection(); + boolean isConcrete = getButton("concrete").getSelection(); SortedNode target = getProfileNode(); Collection args = target.profileExpandArgs(node); if(args.size() < 50 || confirm(node.toString(), args.size())) { - target.profileAddAll(args); + target.profileAddAll(args, isConcrete, arePropertiesRequired); } } }; diff --git a/CIMToolPlugin/src/au/com/langdale/jena/OntModelAdapters.java b/CIMToolPlugin/src/au/com/langdale/jena/OntModelAdapters.java index 0fb93a15..603b316e 100644 --- a/CIMToolPlugin/src/au/com/langdale/jena/OntModelAdapters.java +++ b/CIMToolPlugin/src/au/com/langdale/jena/OntModelAdapters.java @@ -7,8 +7,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; import com.hp.hpl.jena.graph.FrontsNode; +import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.RDF; import au.com.langdale.kena.OntModel; @@ -16,6 +18,7 @@ import au.com.langdale.kena.ResIterator; import au.com.langdale.kena.ResourceFactory; import au.com.langdale.ui.binding.BooleanModel; +import au.com.langdale.xmi.UML; /** * A set of BooleanModel's that reflect the contents of an ontology model. */ @@ -66,6 +69,41 @@ private static class Annotation extends AdapterBase { private FrontsNode ident; private FrontsNode prop; + private static final List NON_EDITABLE_IDENT = new ArrayList(); + private static final List EDITABLE_CLASS_IDENT = new ArrayList(); + private static final List EDITABLE_ASSOCIATION_IDENT = new ArrayList(); + + static { + /** + * Class-level stereotypes - imported via XMI/EAP. None + * of these should be editable by the end user as they + * are core stereotypes native to CIM and used within + * The UML. They would automatically be set on a Class, + * attribute, enumeration, etc. during import processing + * of an XMI or EAP file. + */ + NON_EDITABLE_IDENT.add(UML.primitive.getURI()); + NON_EDITABLE_IDENT.add(UML.datatype.getURI()); + NON_EDITABLE_IDENT.add(UML.cimdatatype.getURI()); + NON_EDITABLE_IDENT.add(UML.enumeration.getURI()); + NON_EDITABLE_IDENT.add(UML.compound.getURI()); + /** attribute-level stereotypes - imported via XMI/EAP */ + NON_EDITABLE_IDENT.add(UML.attribute.getURI()); + NON_EDITABLE_IDENT.add(UML.enumliteral.getURI()); + /** association related stereotypes - imported via XMI/EAP */ + NON_EDITABLE_IDENT.add(UML.compositeOf.getURI()); + NON_EDITABLE_IDENT.add(UML.ofComposite.getURI()); + NON_EDITABLE_IDENT.add(UML.aggregateOf.getURI()); + NON_EDITABLE_IDENT.add(UML.ofAggregate.getURI()); + + /** Stereotypes editable only for top-level Classes in the tree. This doesn't include enumerations. */ + EDITABLE_CLASS_IDENT.add(UML.concrete.getURI()); + EDITABLE_CLASS_IDENT.add(UML.description.getURI()); + + /** Stereotypes editable only for associations in the tree. */ + EDITABLE_ASSOCIATION_IDENT.add(UML.byreference.getURI()); + } + public Annotation(FrontsNode ident, FrontsNode prop, String comment, OntModelProvider context) { super(comment, context); this.ident = ident; @@ -78,12 +116,48 @@ public boolean isTrue() { } public void setTrue(boolean flag) { - OntResource subject = context.getSubject(); - if( subject != null ) - if(flag) + if(isEditable()){ + OntResource subject = context.getSubject(); + if(flag) { subject.addProperty(prop, ident); - else + } else { subject.removeProperty(prop, ident); + } + } + } + + /** + * Method to determine if this Annotation is editable for the currently selected subject. + * The method uses the static List(s) of stereotypes to "test" if a stereotype should be + * allowed to be edited. + */ + public boolean isEditable() { + OntResource subject = context.getSubject(); + if((subject == null) || NON_EDITABLE_IDENT.contains(ident.asNode().getURI()) || // + ((subject != null) && // + // Indicates it is the profile envelope that is selected...nothing is editable + (!subject.isClass() && !subject.isDatatype()) || + // A subject that is a datatype indicates that a attribute of type primitive is currently selected... + (subject.isDatatype() && (EDITABLE_CLASS_IDENT.contains(ident.asNode().getURI()) || EDITABLE_ASSOCIATION_IDENT.contains(ident.asNode().getURI()))) || // + // This is relevant for a top-level class that is selected... + (subject.isClass() && !subject.hasProperty(UML.hasStereotype, UML.attribute) && !subject.hasProperty(OWL.unionOf) && !subject.hasProperty(UML.hasStereotype, UML.enumeration) && EDITABLE_ASSOCIATION_IDENT.contains(ident.asNode().getURI())) || // + // This is relevant for a top-level enumeration that is selected... + (subject.isClass() && !subject.hasProperty(UML.hasStereotype, UML.attribute) && !subject.hasProperty(OWL.unionOf) && subject.hasProperty(UML.hasStereotype, UML.enumeration) && (EDITABLE_CLASS_IDENT.contains(ident.asNode().getURI()) || EDITABLE_ASSOCIATION_IDENT.contains(ident.asNode().getURI()))) || // + // This is relevant for an association that is selected... + (subject.isClass() && !subject.hasProperty(UML.hasStereotype, UML.attribute) && subject.hasProperty(OWL.unionOf) && !subject.hasProperty(UML.hasStereotype, UML.enumeration) && (EDITABLE_CLASS_IDENT.contains(ident.asNode().getURI()))) || // + // Remaining checks are for a non-primitive attribute... + (subject.isClass() && subject.hasProperty(UML.hasStereotype, UML.attribute) && // + (EDITABLE_CLASS_IDENT.contains(ident.asNode().getURI()) || + // The presence of an 'enumeration' stereotype on an attribute indicates that an attribute of that enumeration type is currently selected... + (subject.hasProperty(UML.hasStereotype, UML.enumeration) && EDITABLE_ASSOCIATION_IDENT.contains(ident.asNode().getURI())))))) { + return false; + } + return true; + } + + @Override + public String toString() { + return comment + (!isEditable() ? " [" + "Not Modifiable" + "]" : ""); } } diff --git a/CIMUtil/src/au/com/langdale/jena/JenaTreeModelBase.java b/CIMUtil/src/au/com/langdale/jena/JenaTreeModelBase.java index 7ddb46b2..381bbcf5 100644 --- a/CIMUtil/src/au/com/langdale/jena/JenaTreeModelBase.java +++ b/CIMUtil/src/au/com/langdale/jena/JenaTreeModelBase.java @@ -44,6 +44,14 @@ public JenaTreeModelBase getModel() { public String getPackageName() { return extractPackageName(getBase()); } + + /** + * Provide a name for the package or document that contains this + * definition. + */ + public OntResource getPackage() { + return extractPackage(getBase()); + } } @@ -59,6 +67,11 @@ else if( source != null ){ return ""; } } + + private OntResource extractPackage(OntResource subject) { + OntResource defin = subject.getResource(RDFS.isDefinedBy); + return defin; + } /** * Construct an initially empty tree. diff --git a/CIMUtil/src/au/com/langdale/profiles/ProfileClass.java b/CIMUtil/src/au/com/langdale/profiles/ProfileClass.java index bba557bf..109c1bab 100644 --- a/CIMUtil/src/au/com/langdale/profiles/ProfileClass.java +++ b/CIMUtil/src/au/com/langdale/profiles/ProfileClass.java @@ -35,6 +35,7 @@ public class ProfileClass { private OneToManyMap props; private OntResource baseClass; private Set classes; + private boolean compoundBase; private boolean enumeratedBase; private final OntResource defaultBase; @@ -92,7 +93,33 @@ private void analyseBaseClass() { // System.out.println("Profile with no schema class:"); // System.out.println( clss.describe()); // } + compoundBase = baseClass.hasProperty(UML.hasStereotype, UML.compound); enumeratedBase = baseClass.hasProperty(UML.hasStereotype, UML.enumeration); + if (baseClass != null) { + if (enumeratedBase) { + if (baseClass.hasProperty(UML.hasStereotype)) { + ResIterator stereotypes = baseClass.listProperties(UML.hasStereotype); + while (stereotypes.hasNext()) { + OntResource stereo = stereotypes.nextResource(); + if (!clss.hasProperty(UML.hasStereotype, stereo)) + clss.addProperty(UML.hasStereotype, stereo); + } + } + } else { + ResIterator clazzes = baseClass.listSuperClasses(true); + while (clazzes.hasNext()) { + OntResource clazz = clazzes.nextResource(); + if (clazz.hasProperty(UML.hasStereotype)) { + ResIterator stereotypes = clazz.listProperties(UML.hasStereotype); + while (stereotypes.hasNext()) { + OntResource stereo = stereotypes.nextResource(); + if (!clss.hasProperty(UML.hasStereotype, stereo)) + clss.addProperty(UML.hasStereotype, stereo); + } + } + } + } + } } /** @@ -264,6 +291,15 @@ public OntResource createAllValuesFrom(OntResource prop, boolean required) { label = prop.getLocalName(); child.addLabel(label, null); + if (prop.hasProperty(UML.hasStereotype)) { + ResIterator stereotypes = prop.listProperties(UML.hasStereotype); + while (stereotypes.hasNext()) { + OntResource stereo = stereotypes.nextResource(); + if (!child.hasProperty(UML.hasStereotype, stereo)) + child.addProperty(UML.hasStereotype, stereo); + } + } + OntResource res = model.createAllValuesFromRestriction(null, prop, child); clss.addSuperClass(res); props.put(prop, res); @@ -395,6 +431,10 @@ public boolean isEnumerated() { return enumeratedBase; } + public boolean isCompound() { + return compoundBase; + } + public boolean isReference() { return hasStereotype(UML.byreference) || clss.hasSuperClass(MESSAGE.Reference, false); } diff --git a/CIMUtil/src/au/com/langdale/profiles/ProfileModel.java b/CIMUtil/src/au/com/langdale/profiles/ProfileModel.java index 831ef2f1..f748b531 100644 --- a/CIMUtil/src/au/com/langdale/profiles/ProfileModel.java +++ b/CIMUtil/src/au/com/langdale/profiles/ProfileModel.java @@ -53,6 +53,10 @@ public void setBackgroundModel(OntModel backgroundModel) { initModels(); } + public String getOntologyNamespace() { + return (this.backgroundModel != null && this.backgroundModel.getValidOntology() != null ? this.backgroundModel.getValidOntology().getURI() : ""); + } + @Override public void setOntModel(OntModel profileModel) { super.setOntModel(null); @@ -161,14 +165,15 @@ public void setComment(String text) { getSubject().setComment(text, null); } - protected abstract void create(Node node); + protected abstract void create(Node node, boolean isConcrete, boolean arePropertiesRequired); - protected void createAnon(Node node) { - create(node); + protected void createAnon(Node node, boolean isConcrete, boolean arePropertiesRequired) { + // Anon classes are never concrete... + create(node, false, arePropertiesRequired); } - protected void createDeep(Node node) { - create(node); + protected void createDeep(Node node, boolean isConcrete, boolean arePropertiesRequired) { + create(node, isConcrete, arePropertiesRequired); } protected abstract void destroy(); @@ -184,20 +189,20 @@ public void profileRemove(Node node) { } - public void profileAddAll(Collection args) { + public void profileAddAll(Collection args, boolean isConcrete, boolean arePropertiesRequired) { for (Iterator it = args.iterator(); it.hasNext();) - create((Node) it.next()); + create((Node) it.next(), isConcrete, arePropertiesRequired); structureChanged(); } - public void profileAddAllDeep(Collection args) { + public void profileAddAllDeep(Collection args, boolean isConcrete, boolean arePropertiesRequired) { for (Iterator it = args.iterator(); it.hasNext();) - createDeep((Node) it.next()); + createDeep((Node) it.next(), isConcrete, arePropertiesRequired); getRoot().structureChanged(); } - public void profileAddAnon(Node node) { - createAnon(node); + public void profileAddAnon(Node node, boolean isConcrete, boolean isRequired) { + createAnon(node, false, isRequired); structureChanged(); } @@ -248,17 +253,17 @@ protected void destroy() { * Create a new named class derived from the given class. */ @Override - protected void create(Node node) { + protected void create(Node node, boolean isConcrete, boolean isRequired) { OntResource base = node.getSubject(); if( base.isClass()) - getRefactory().createProfileClass(base); + getRefactory().createProfileClass(base, isConcrete); } @Override - protected void createDeep(Node node) { + protected void createDeep(Node node, boolean isConcrete, boolean isRequired) { OntResource base = node.getSubject(); if( base.isClass()) { - getRefactory().createCompleteProfile(base, true); + getRefactory().createCompleteProfile(base, isConcrete, isRequired); } } @@ -363,6 +368,10 @@ public void structureChanged() { public boolean isEnumerated() { return profile.isEnumerated(); } + + public boolean isCompound() { + return profile.isCompound(); + } public ProfileClass getProfile() { return profile; @@ -431,10 +440,10 @@ protected String collation() { @Override public Class getIconClass() { - if( profile.isReference()) - return ReferenceNode.class; - else if(prop.isDatatypeProperty()) + if(prop.isDatatypeProperty()) return AttributeNode.class; + else if(profile.isReference()) + return ReferenceNode.class; else return ElementNode.class; } @@ -535,7 +544,7 @@ public Collection profileExpandArgs(Node node) { } @Override - protected void create(Node node) { + protected void create(Node node, boolean isConcrete, boolean arePropertiesRequired) { OntResource base = node.getSubject(); if( base.isClass() && ! isDatatype()) { OntResource member = getRefactory().findOrCreateNamedProfile(base); @@ -544,7 +553,7 @@ protected void create(Node node) { } @Override - protected void createAnon(Node node) { + protected void createAnon(Node node, boolean isConcrete, boolean arePropertiesRequired) { OntResource base = node.getSubject(); if( base.isClass() && ! isDatatype()) { profile.createUnionMember(base); @@ -626,7 +635,7 @@ public void setComment(String name) { } @Override - protected void create(Node node) { + protected void create(Node node, boolean isConcrete, boolean arePropertiesRequired) { // can't add children } } @@ -639,10 +648,10 @@ protected NaturalNode(ProfileClass profile) { * Create a child element in the underlying ontology. */ @Override - protected void create(Node node) { + protected void create(Node node, boolean isConcrete, boolean arePropertiesRequired) { OntResource base = node.getSubject(); if( base.isProperty()) { - profile.createAllValuesFrom(base, true); + profile.createAllValuesFrom(base, arePropertiesRequired); } else if( base.hasRDFType(profile.getBaseClass())) { profile.addIndividual(base); @@ -650,10 +659,10 @@ else if( base.hasRDFType(profile.getBaseClass())) { } @Override - protected void createDeep(Node node) { + protected void createDeep(Node node, boolean isConcrete, boolean arePropertiesRequired) { OntResource prop = node.getSubject(); if( prop.isProperty()) { - profile.createAllValuesFrom(prop, true); + profile.createAllValuesFrom(prop, arePropertiesRequired); getRefactory().createDefaultRange(profile, prop); } } @@ -705,12 +714,14 @@ public GeneralTypeNode(ProfileClass profile) { @Override public Class getIconClass() { - if(hasStereotype(UML.concrete)) - return RootElementNode.class; - else if(hasStereotype(UML.compound)) + if(hasStereotype(UML.compound)) return CompoundElementNode.class; else if(hasStereotype(UML.enumeration)) return EnumElementNode.class; + else if(hasStereotype(UML.concrete) && hasStereotype(UML.description)) + return DescriptorRootElementNode.class; + else if(hasStereotype(UML.concrete)) + return RootElementNode.class; else if( profile.getSubject().isAnon()) return AnonTypeNode.class; else @@ -814,7 +825,7 @@ protected void populate() { } @Override - protected void create(Node node) { + protected void create(Node node, boolean isConcrete, boolean arePropertiesRequired) { OntResource type = node.getSubject(); if( ! type.isClass()) return; @@ -834,6 +845,12 @@ public interface AttributeNode {} * */ public interface ReferenceNode {} + + /** + * A marker class returned for concrete type nodes that also are marked as 'descriptors'; + * + */ + public interface DescriptorRootElementNode {} /** * A marker class returned for concrete type nodes; diff --git a/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java b/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java index cfc61300..182b2a3c 100644 --- a/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java +++ b/CIMUtil/src/au/com/langdale/profiles/ProfileSerializer.java @@ -9,6 +9,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.SortedMap; +import java.util.TreeMap; import java.util.regex.Pattern; import javax.xml.transform.ErrorListener; @@ -35,6 +37,7 @@ import au.com.langdale.jena.TreeModelBase.Node; import au.com.langdale.kena.OntResource; import au.com.langdale.kena.ResIterator; +import au.com.langdale.profiles.ProfileClass.PropertyInfo; import au.com.langdale.profiles.ProfileModel.CatalogNode; import au.com.langdale.profiles.ProfileModel.EnvelopeNode; import au.com.langdale.profiles.ProfileModel.EnvelopeNode.MessageNode; @@ -46,29 +49,31 @@ import au.com.langdale.sax.AbstractReader; import au.com.langdale.xmi.UML; +import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.XSD; /** - * Convert a message model to XML. The form of the XML is designed - * to be easily transformed to specific schemas in any schema language. - * - * A MessageSerializer is a SAX XMLReader. Calling parse(InputSource) - * causes it to emit SAX events representing a message definition. + * Convert a message model to XML. The form of the XML is designed to be easily + * transformed to specific schemas in any schema language. * - * However, the write() method is a more convenient way to generate - * XML to a file. - * - * If setStylesheet() is called before write, the output is transformed - * into a schema according to the given templates. + * A MessageSerializer is a SAX XMLReader. Calling parse(InputSource) causes it + * to emit SAX events representing a message definition. + * + * However, the write() method is a more convenient way to generate XML to a + * file. + * + * If setStylesheet() is called before write, the output is transformed into a + * schema according to the given templates. */ public class ProfileSerializer extends AbstractReader { public static final String XSDGEN = "http://langdale.com.au/2005/xsdgen"; private JenaTreeModelBase model; private String baseURI = ""; + private String ontologyURI = ""; private String version = ""; private String copyrightMultiLine = ""; private String copyrightSingleLine = ""; - private ArrayList templates = new ArrayList(); + private ArrayList templates = new ArrayList(); TransformerFactory factory = TransformerFactory.newInstance(); private final String xsd = XSD.anyURI.getNameSpace(); private HashSet deferred = new HashSet(); @@ -76,14 +81,13 @@ public class ProfileSerializer extends AbstractReader { /** * Construct a serializer for the given MessageModel. */ - public ProfileSerializer(JenaTreeModelBase model) { + public ProfileSerializer(ProfileModel model) { this.model = model; } - + /** - * A URI that is passed as the baseURI attribute of the root element - * and is generally used to establish a namespace for the generated - * schema. + * A URI that is passed as the baseURI attribute of the root element and is + * generally used to establish a namespace for the generated schema. */ public String getBaseURI() { return baseURI; @@ -95,13 +99,31 @@ public String getBaseURI() { public void setBaseURI(String baseURI) { this.baseURI = baseURI; } - + + /** + * A URI that is passed as the onotologyURI attribute of the root element of + * the ontology from which the profile model was derived from. This is + * generally used to establish an xml:base URI that may be necessary for + * RDF-based schema that may be generated. + */ + public String getOntologyURI() { + return ontologyURI; + } + + /** + * Set the ontologies base URI (see above). + */ + public void setOntologyURI(String ontologyURI) { + if (ontologyURI != null) + this.ontologyURI = ontologyURI; + } + /** - * A single-line copyright notification including a year. It will be passed as - * an attribute named "coyright-single-line" to the serializer therefore making it - * available to a profile builder for use as the copyright notice within the generated - * schema if the schema builder desires a single lined copyright as opposed to a - * multi-line copyright. + * A single-line copyright notification including a year. It will be passed + * as an attribute named "coyright-single-line" to the serializer therefore + * making it available to a profile builder for use as the copyright notice + * within the generated schema if the schema builder desires a single lined + * copyright as opposed to a multi-line copyright. */ public String getCopyrightSingleLine() { return copyrightSingleLine; @@ -113,11 +135,12 @@ public String getCopyrightSingleLine() { public void setCopyrightSingleLine(String copyrightSingleLine) { this.copyrightSingleLine = copyrightSingleLine; } - + /** - * A multi-line copyright notification including a year. It will be passed as - * an attribute named "coyright" to the serializer therefore making it available - * to a profile builder for use as the copyright notice within the generated schema. + * A multi-line copyright notification including a year. It will be passed + * as an attribute named "coyright" to the serializer therefore making it + * available to a profile builder for use as the copyright notice within the + * generated schema. */ public String getCopyrightMultiLine() { return copyrightMultiLine; @@ -129,182 +152,210 @@ public String getCopyrightMultiLine() { public void setCopyrightMultiLine(String copyright) { this.copyrightMultiLine = copyright; } - + /** - * Install a stylesheet to transform the abstract message definition to a schema. + * Install a stylesheet to transform the abstract message definition to a + * schema. */ - public void setStyleSheet(InputStream s, String base) throws TransformerConfigurationException { + public void setStyleSheet(InputStream s, String base) + throws TransformerConfigurationException { templates.clear(); addStyleSheet(s, base); - } /** - * Install a stylesheet to apply after any previously installed stylesheets. + * Install a stylesheet to apply after any previously installed stylesheets. */ - public void addStyleSheet(InputStream s, String base) throws TransformerConfigurationException { - templates.add( factory.newTemplates(new StreamSource(s, base))); + public void addStyleSheet(InputStream s, String base) + throws TransformerConfigurationException { + templates.add(factory.newTemplates(new StreamSource(s, base))); } - + /** * Install a stylesheet from the standard set. Use null for no stylesheet. * - * When the name is null it indicates that no stylesheets are to be applied. - * In this scenario the output of a call to the ProfileSerializer's write() - * method will simply be the output of the CIMTool's internal message model - * as a formatted XML document. Therefore, any existing templates are "cleared" - * and will apply only the "indent-xml.xsl" stylesheet to format the XML when - * written. This stylesheet is for internal purposes only. + * When the name is null it indicates that no stylesheets are to be applied. + * In this scenario the output of a call to the ProfileSerializer's write() + * method will simply be the output of the CIMTool's internal message model + * as a formatted XML document. Therefore, any existing templates are + * "cleared" and will apply only the "indent-xml.xsl" stylesheet to format + * the XML when written. This stylesheet is for internal purposes only. */ - public void setStyleSheet(String name) throws TransformerConfigurationException { - if( name == null) { + public void setStyleSheet(String name) + throws TransformerConfigurationException { + if (name == null) { templates.clear(); - setStyleSheet(getClass().getResourceAsStream("indent-xml.xsl"), XSDGEN); - } - else { + setStyleSheet(getClass().getResourceAsStream("indent-xml.xsl"), + XSDGEN); + } else { setStyleSheet(getClass().getResourceAsStream(name + ".xsl"), XSDGEN); } } /** - * Install a stylesheet to apply after any previously installed stylesheets. + * Install a stylesheet to apply after any previously installed stylesheets. */ - public void addStyleSheet(String name) throws TransformerConfigurationException { + public void addStyleSheet(String name) + throws TransformerConfigurationException { if (name != null) addStyleSheet(getClass().getResourceAsStream(name + ".xsl"), ""); } - + /** - * Install a standard SAX ErrorHandler for errors in the stylesheet. - * This will be wrapped in the ErrorListener required by the transform - * framework. + * Install a standard SAX ErrorHandler for errors in the stylesheet. This + * will be wrapped in the ErrorListener required by the transform framework. */ @Override public void setErrorHandler(final ErrorHandler errors) { ErrorListener listener = new ErrorListener() { - public void error(TransformerException ex) throws TransformerException { + public void error(TransformerException ex) + throws TransformerException { try { errors.error(convert(ex)); - } - catch (SAXException e) { + } catch (SAXException e) { throw convert(e); } } - public void warning(TransformerException ex) throws TransformerException { + public void warning(TransformerException ex) + throws TransformerException { try { errors.warning(convert(ex)); - } - catch (SAXException e) { + } catch (SAXException e) { throw convert(e); } } - public void fatalError(TransformerException ex) throws TransformerException { + public void fatalError(TransformerException ex) + throws TransformerException { try { errors.fatalError(convert(ex)); - } - catch (SAXException e) { + } catch (SAXException e) { throw convert(e); } } - + private SAXParseException convert(TransformerException ex) { Throwable cause = ex.getCause(); - if( cause instanceof SAXParseException) - return (SAXParseException)cause; - + if (cause instanceof SAXParseException) + return (SAXParseException) cause; + SourceLocator loc = ex.getLocator(); - if(loc != null) - return new SAXParseException(ex.getMessage(), loc.getPublicId(), loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber()); + if (loc != null) + return new SAXParseException(ex.getMessage(), + loc.getPublicId(), loc.getSystemId(), + loc.getLineNumber(), loc.getColumnNumber()); return new SAXParseException(ex.getMessage(), "", "", 0, 0); } - + private TransformerException convert(SAXException ex) { return new TransformerException(ex.getMessage(), ex); } }; - + factory.setErrorListener(listener); } /** - * Generate a schema from the message definition - * and write it to the given stream. + * Generate a schema from the message definition and write it to the given + * stream. */ @Override public void write(OutputStream ostream) throws TransformerException { - Transformer[] tx; - if( ! templates.isEmpty()) { + Transformer[] tx; + if (!templates.isEmpty()) { tx = new Transformer[templates.size()]; - for( int ix = 0; ix < templates.size(); ix++) { - Transformer ti = ((Templates)templates.get(ix)).newTransformer(); - ti.setParameter("baseURI", baseURI); - ti.setParameter("version", version); - ti.setParameter("envelope", model.getRoot().getName()); - ti.setParameter("copyright", copyrightMultiLine); - ti.setParameter("copyright-single-line", copyrightSingleLine); - tx[ix] = ti; + for (int ix = 0; ix < templates.size(); ix++) { + Transformer ti = ((Templates) templates.get(ix)) + .newTransformer(); + ti.setParameter("baseURI", baseURI); + ti.setParameter("ontologyURI", ontologyURI); + ti.setParameter("version", version); + ti.setParameter("envelope", model.getRoot().getName()); + ti.setParameter("copyright", copyrightMultiLine); + ti.setParameter("copyright-single-line", copyrightSingleLine); + tx[ix] = ti; } - } - else { + } else { tx = new Transformer[] { factory.newTransformer() }; } - + Result result = new StreamResult(ostream); Source source = new SAXSource(this, new InputSource()); - for( int ix = 0; ix < tx.length-1; ix++ ) { - DOMResult inter = new DOMResult(); + for (int ix = 0; ix < tx.length - 1; ix++) { + DOMResult inter = new DOMResult(); tx[0].transform(source, inter); source = new DOMSource(inter.getNode()); } - tx[tx.length-1].transform(source, result); + tx[tx.length - 1].transform(source, result); + } + + protected void emit() throws SAXException { + emit(model.getRoot()); + } + + private void emitPackages(CatalogNode node) throws SAXException { + SortedMap packages = new TreeMap(); + Iterator it = node.iterator(); + while (it.hasNext()) { + Node nextNode = (Node) it.next(); + if (nextNode instanceof TypeNode) { + TypeNode typeNode = (TypeNode) nextNode; + OntResource aPackage = typeNode.getPackage(); + while (aPackage != null) { + if (!packages.containsKey(aPackage.getURI())) + packages.put(aPackage.getURI(), aPackage); + aPackage = aPackage.getIsDefinedBy(); + } + } + } + for (OntResource aPackage : packages.values()) { + emitPackage(aPackage); + } } - protected void emit() throws SAXException { - emit(model.getRoot()); - } - private void emit(Node node) throws SAXException { - if( node instanceof CatalogNode) - emit((CatalogNode)node); - else if( node instanceof EnvelopeNode) - emit((EnvelopeNode)node); - else if( node instanceof MessageNode) - emit((MessageNode)node); - else if( node instanceof TypeNode) - emit((TypeNode)node); - else if( node instanceof SuperTypeNode) - emit((SuperTypeNode)node); - else if( node instanceof ElementNode) - emit((ElementNode)node); - else if( node instanceof EnumValueNode) - emit((EnumValueNode)node); - else if( node instanceof SubTypeNode) - emit((SubTypeNode)node); - else if( node != null) + if (node instanceof CatalogNode) + emit((CatalogNode) node); + else if (node instanceof EnvelopeNode) + emit((EnvelopeNode) node); + else if (node instanceof MessageNode) + emit((MessageNode) node); + else if (node instanceof TypeNode) + emit((TypeNode) node); + else if (node instanceof SuperTypeNode) + emit((SuperTypeNode) node); + else if (node instanceof ElementNode) + emit((ElementNode) node); + else if (node instanceof EnumValueNode) + emit((EnumValueNode) node); + else if (node instanceof SubTypeNode) + emit((SubTypeNode) node); + else if (node != null) emitChildren(node); } - + private void emitChildren(Node node) throws SAXException { Iterator it = node.iterator(); - while( it.hasNext()) - emit((Node)it.next()); + while (it.hasNext()) + emit((Node) it.next()); } private void emit(CatalogNode node) throws SAXException { Element elem = new Element("Catalog", MESSAGE.NS); + elem.set("ontologyURI", ontologyURI); elem.set("baseURI", baseURI); elem.set("xmlns:m", baseURI); elem.set("name", node.getName()); emitNote(node); + emitPackages(node); emitChildren(node); - + Iterator it = deferred.iterator(); - while( it.hasNext()) { - emit((OntResource)it.next()); + while (it.hasNext()) { + emit((OntResource) it.next()); } elem.close(); } @@ -319,58 +370,59 @@ private void emit(EnvelopeNode node) throws SAXException { private void emit(SuperTypeNode node) throws SAXException { Element elem = new Element("SuperType"); elem.set("name", node.getName()); + elem.set("baseClass", node.getBaseClass().getURI()); elem.close(); } - + private void emit(SubTypeNode node) throws SAXException { Element elem = select(node); elem.set("name", node.getName()); elem.set("minOccurs", "1"); elem.set("maxOccurs", "1"); emitNote(node); - if(node.getSubject().isAnon()) + if (node.getSubject().isAnon()) emitChildren(node); elem.close(); } - + private Element select(SubTypeNode node) throws SAXException { Element elem; - boolean anon = node.getSubject().isAnon(); - if( node.isEnumerated()) { - if(anon) + boolean anon = node.getSubject().isAnon(); + if (node.isEnumerated()) { + if (anon) elem = new Element("SimpleEnumerated"); - else + else elem = new Element("Enumerated"); - } - else { - if( anon ) + } else { + if (anon) elem = new Element("Complex"); else { - if( node.getParent() instanceof ElementNode && ((ElementNode)node.getParent()).isReference()) - elem = new Element("Reference"); + if (node.getParent() instanceof ElementNode + && ((ElementNode) node.getParent()).isReference()) + elem = new Element("Reference"); else elem = new Element("Instance"); } } - + elem.set("baseClass", node.getBaseClass().getURI()); - if( ! anon ) + if (!anon) elem.set("type", node.getName()); return elem; } - + private void emit(ElementNode node) throws SAXException { Element elem; - - if( node.isDatatype() ) { + + 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 == null) + range = model.getOntModel() + .createResource(XSD.xstring.asNode()); + if (range.getNameSpace().equals(xsd)) { elem = new Element("Simple"); - } - else { + } else { elem = new Element("Domain"); elem.set("type", range.getLocalName()); deferred.add(range); @@ -378,83 +430,143 @@ private void emit(ElementNode node) throws SAXException { elem.set("dataType", range.getURI()); elem.set("xstype", xstype(range)); emit(node, elem); - } - else { - + } else { + int size = node.getChildren().size(); - - if( size == 0 ) { + + if (size == 0) { OntResource range = node.getBaseProperty().getRange(); - elem = new Element("Reference"); - if( range != null && range.isURIResource()) + elem = new Element("Reference"); + if (range != null && range.isURIResource()) elem.set("type", range.getLocalName()); emit(node, elem); - } - else if( size == 1) { + + OntResource inverse = (node.getBaseProperty() != null ? node.getBaseProperty().getInverse() : null); + if ((inverse != null) && (node.getProfile().getPropertyInfo(inverse) != null)) { + elem.close(); // First, close "Reference" ... + elem = new Element("InverseReference"); + emitInverse(node, elem); + } + } else if (size == 1) { SubTypeNode child = (SubTypeNode) node.getChildren().get(0); elem = select(child); emit(node, elem); - if(child.getSubject().isAnon()) + if (child.getSubject().isAnon()) { emitChildren(child); - } - else { + } else if (!child.isEnumerated()) { + // If child is not an enumeration and not anonymous it indicates it is an Instance + // or Reference element and an InverseInstance or InverseReference must be created... + OntResource inverse = (node.getBaseProperty() != null ? node.getBaseProperty().getInverse() : null); + if ((inverse != null) && (node.getProfile().getPropertyInfo(inverse) != null)) { + elem.close(); // First, close out the "Reference" or "Instance" element... + if (child.getParent() instanceof ElementNode && ((ElementNode) child.getParent()).isReference()) { + elem = new Element("InverseReference"); + } else { + elem = new Element("InverseInstance"); + } + emitInverse(node, elem); + } + } + } else { elem = new Element("Choice"); emitChoice(node, elem); emitChildren(node); } - } - + elem.close(); } + private void emitInverse(ElementNode node, Element elem) throws SAXException { + OntResource inverse = (node.getBaseProperty() != null ? node + .getBaseProperty().getInverse() : null); + + if ((inverse != null) && (node.getProfile().getPropertyInfo(inverse) != null)) { + elem.set("baseClass", inverse.getRange().getURI()); + elem.set("type", inverse.getRange().getLocalName()); + if (inverse.getLabel() != null) + elem.set("name", inverse.getLabel()); + if (inverse.getURI() != null) + elem.set("baseProperty", inverse.getURI()); + + PropertyInfo info = node.getProfile().getPropertyInfo(inverse); + if (!info.getProperty().isDatatypeProperty()) { + int min = info.getMinCardinality(); + int max = info.getMaxCardinality(); + elem.set("minOccurs", ProfileModel.cardString(min)); + elem.set("maxOccurs", + ProfileModel.cardString(max, "unbounded")); + } + + elem.set("inverseBaseProperty", node.getBaseProperty().getURI()); + + emitComment(info.getProperty().getComment(null)); + emitStereotypes(info.getProperty()); + } + } + private void emit(ElementNode node, Element elem) throws SAXException { elem.set("name", node.getName()); elem.set("baseProperty", node.getBaseProperty().getURI()); elem.set("minOccurs", ProfileModel.cardString(node.getMinCardinality())); - elem.set("maxOccurs", ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); + elem.set("maxOccurs", + ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); + + OntResource inverse = (node.getBaseProperty() != null ? node + .getBaseProperty().getInverse() : null); + if ((inverse != null) && (inverse.getURI() != null)) { + elem.set("inverseBaseProperty", inverse.getURI()); + } - emit(node.getBaseProperty().getComment(null)); + emitComment(node.getBaseProperty().getComment(null)); emitNote(node); } - + private void emitChoice(ElementNode node, Element elem) throws SAXException { elem.set("name", node.getName()); elem.set("baseProperty", node.getBaseProperty().getURI()); elem.set("minOccurs", ProfileModel.cardString(node.getMinCardinality())); - elem.set("maxOccurs", ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); + elem.set("maxOccurs", + ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); // - // We obtain the topmost class in the hierarchy via the call to node.getBaseClass(). - // We then loop through all of the child nodes of the choice and locate the one that - // matches the base class associated with the Choice property. This is the one we must - // use when generating the 'inheritanceBaseClass' and 'inheritanceBaseType' attributes - // for the Choice. These two attributes indicate the URI of the topmost base class in - // the hierarchy and the name of that class as specified in the context of the profile + // We obtain the topmost class in the hierarchy via the call to + // node.getBaseClass(). + // We then loop through all of the child nodes of the choice and locate + // the one that + // matches the base class associated with the Choice property. This is + // the one we must + // use when generating the 'inheritanceBaseClass' and + // 'inheritanceBaseType' attributes + // for the Choice. These two attributes indicate the URI of the topmost + // base class in + // the hierarchy and the name of that class as specified in the context + // of the profile // defined. // OntResource baseClass = node.getBaseClass(); Iterator it = node.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { Node childNode = (Node) it.next(); if (childNode.getBase().equals(baseClass)) { - elem.set("inheritanceBaseClass", childNode.getSubject().getURI()); + elem.set("inheritanceBaseClass", childNode.getSubject() + .getURI()); elem.set("inheritanceBaseType", childNode.getName()); break; } } - emit(node.getBaseProperty().getComment(null)); + emitComment(node.getBaseProperty().getComment(null)); emitNote(node); } - private void emit(String comment) throws SAXException { + private void emitComment(String comment) throws SAXException { emit("Comment", comment); } - + private static Pattern delimiter = Pattern.compile(" *([\r\n] *)+"); private void emit(String ename, String comment) throws SAXException { - if( comment == null) + if (comment == null) return; String[] pars = delimiter.split(comment.trim()); for (int ix = 0; ix < pars.length; ix++) { @@ -468,7 +580,7 @@ private void emitNote(Node node) throws SAXException { emit("Note", node.getSubject().getComment(null)); emitStereotypes(node.getSubject()); } - + private void emitStereotypes(OntResource subject) throws SAXException { ResIterator it = subject.listProperties(UML.hasStereotype); while (it.hasNext()) { @@ -477,41 +589,65 @@ private void emitStereotypes(OntResource subject) throws SAXException { } private void emitStereotype(OntResource stereo) throws SAXException { - if( ! stereo.isURIResource()) + if (!stereo.isURIResource()) return; Element elem = new Element("Stereotype"); + elem.set("label", stereo.getLabel()); elem.append(stereo.getURI()); elem.close(); } private String xstype(OntResource type) { - if(type.getNameSpace().equals(xsd)) + if (type.getNameSpace().equals(xsd)) return type.getLocalName(); - + OntResource xtype = type.getEquivalentClass(); - if( xtype != null && xtype.getNameSpace().equals(xsd)) + if (xtype != null && xtype.getNameSpace().equals(xsd)) return xtype.getLocalName(); System.out.println("Warning: undefined datatype: " + type); return "string"; } + private void emitPackage(OntResource aPackage) throws SAXException { + if (aPackage != null) { + Element elem = new Element("Package"); + elem.set("name", aPackage.getLabel()); + elem.set("basePackage", aPackage.getURI()); + emitComment(aPackage.getComment(null)); + emitStereotypes(aPackage); + // Determine if there is an owner package... + if (aPackage.getIsDefinedBy() != null) { + OntResource theOwnerPackage = aPackage.getIsDefinedBy(); + Element parentPackageElem = new Element("ParentPackage"); + parentPackageElem.set("name", theOwnerPackage.getLabel()); + parentPackageElem.set("basePackage", theOwnerPackage.getURI()); + parentPackageElem.close(); + } + elem.close(); + } + } + private void emit(TypeNode node) throws SAXException { - Element elem; - if( node.hasStereotype(UML.concrete)) + Element elem; + if (node.hasStereotype(UML.concrete)) elem = new Element("Root"); - else if(node.isEnumerated()) + else if (node.isEnumerated()) elem = new Element("EnumeratedType"); - else if( node.hasStereotype(UML.compound)) + else if (node.hasStereotype(UML.compound)) elem = new Element("CompoundType"); else elem = new Element("ComplexType"); elem.set("name", node.getName()); elem.set("baseClass", node.getBaseClass().getURI()); elem.set("package", node.getPackageName()); + if (node.getPackage() != null) { + elem.set("packageURI", node.getPackage().getURI()); + } elem.set("minOccurs", ProfileModel.cardString(node.getMinCardinality())); - elem.set("maxOccurs", ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); - emit(node.getBaseClass().getComment(null)); + elem.set("maxOccurs", + ProfileModel.cardString(node.getMaxCardinality(), "unbounded")); + emitComment(node.getBaseClass().getComment(null)); emitNote(node); emitChildren(node); @@ -523,7 +659,7 @@ private void emit(MessageNode node) throws SAXException { elem.set("name", node.getName()); elem.set("baseClass", node.getBaseClass().getURI()); - emit(node.getBaseClass().getComment(null)); + emitComment(node.getBaseClass().getComment(null)); emitNote(node); emitChildren(node); @@ -532,22 +668,27 @@ private void emit(MessageNode node) throws SAXException { private void emit(OntResource type) throws SAXException { Element elem = new Element("SimpleType"); + elem.set("dataType", type.getURI()); elem.set("name", type.getLocalName()); + OntResource defin = type.getResource(RDFS.isDefinedBy); + if( defin != null ){ + elem.set("package", defin.getLabel()); + elem.set("packageURI", defin.getURI()); + } elem.set("xstype", xstype(type)); - emit(type.getComment(null)); + emitComment(type.getComment(null)); elem.close(); } private void emit(EnumValueNode node) throws SAXException { OntResource value = node.getSubject(); - Element elem = new Element("EnumeratedValue"); elem.set("name", node.getName()); - elem.set("baseResource", node.getBase().getURI()); - emit(value.getComment(null)); + elem.set("baseResource", node.getBase().getURI()); + emitComment(value.getComment(null)); elem.close(); } @@ -558,4 +699,5 @@ public String getVersion() { public void setVersion(String version) { this.version = version; } + } diff --git a/CIMUtil/src/au/com/langdale/profiles/ProfileUtility.java b/CIMUtil/src/au/com/langdale/profiles/ProfileUtility.java index 798b1fde..4f47c09b 100644 --- a/CIMUtil/src/au/com/langdale/profiles/ProfileUtility.java +++ b/CIMUtil/src/au/com/langdale/profiles/ProfileUtility.java @@ -187,7 +187,7 @@ private void addRestriction(PropertySpec other) { */ public static class PropertySpec { public final OntResource prop; - public final boolean required, functional, reference; + public final boolean required, functional, reference, compound; public final OntResource base_range, base_domain; // FIXME: base_range should be OntResource public final String label, comment; @@ -196,6 +196,7 @@ public PropertySpec(PropertyInfo info, ProfileClass range_profile) { required = info.isRequired(); functional = info.isFunctional(); reference = range_profile != null && range_profile.isReference(); + compound = range_profile != null && range_profile.isCompound(); // repair domain and range base_domain = selectType(prop.getDomain(), info.getDomainProfile().getBaseClass()); @@ -213,7 +214,7 @@ public PropertySpec(PropertyInfo info, ProfileClass range_profile) { public PropertySpec(OntResource prop, OntResource domain, OntResource range) { this.prop = prop; - required = reference = false; + required = reference = compound = false; functional = prop.isFunctionalProperty() || prop.isDatatypeProperty(); base_domain = selectType(prop.getDomain(), domain); base_range = selectType(prop.getRange(), range); @@ -232,6 +233,7 @@ private PropertySpec(PropertySpec lhs, PropertySpec rhs) { functional = lhs.functional || rhs.functional; reference = lhs.reference || rhs.reference; required = lhs.required || rhs.required; + compound = lhs.compound || rhs.compound; base_range = mergeRange(prop.getRange(), lhs.base_range, rhs.base_range); // take the profile label if both sides agree diff --git a/CIMUtil/src/au/com/langdale/profiles/Refactory.java b/CIMUtil/src/au/com/langdale/profiles/Refactory.java index d5379a1e..f9f5bc51 100644 --- a/CIMUtil/src/au/com/langdale/profiles/Refactory.java +++ b/CIMUtil/src/au/com/langdale/profiles/Refactory.java @@ -211,6 +211,14 @@ public OntResource findOrCreateNamedProfile(OntResource base) { if( clss == null ) { clss = getModel().createClass(getNamespace() + base.getLocalName()); clss.addSuperClass(base); + if (base.hasProperty(UML.hasStereotype)) { + ResIterator stereotypes = base.listProperties(UML.hasStereotype); + while (stereotypes.hasNext()) { + OntResource stereo = stereotypes.nextResource(); + if (!clss.hasProperty(UML.hasStereotype, stereo)) + clss.addProperty(UML.hasStereotype, stereo); + } + } add(clss, base, true); } return clss; @@ -229,7 +237,7 @@ public Collection findRelatedProfiles(OntResource base, boolean subclass, boolea return map.findRelatedProfiles(base, subclass, unique); } - public OntResource createProfileClass(OntResource base) { + public OntResource createProfileClass(OntResource base, boolean isConcrete) { String uri = getNamespace() + base.getLocalName(); OntResource probe = getModel().createResource(uri); @@ -241,6 +249,9 @@ public OntResource createProfileClass(OntResource base) { OntResource child = getModel().createClass(probe.getURI()); child.addSuperClass(base); + // Concrete stereotypes are applicable only to non-enumerated classes + if (isConcrete && !base.hasProperty(UML.hasStereotype, UML.enumeration)) + child.addProperty(UML.hasStereotype, UML.concrete); add(child, base, ix == 1); return child; } @@ -253,23 +264,23 @@ public void createDefaultRange(ProfileClass prof, OntResource prop) { } } - public void createAllProperties(ProfileClass profile, boolean required) { + public void createAllProperties(ProfileClass profile, boolean arePropertiesRequired) { ResIterator it = getModel().listSubjectsWithProperty(RDFS.domain, profile.getBaseClass()); while( it.hasNext()) { OntResource prop = it.nextResource(); if( prop.isFunctionalProperty()) { - profile.createAllValuesFrom(prop, required); + profile.createAllValuesFrom(prop, arePropertiesRequired); createDefaultRange(profile, prop); } } } - public void createCompleteProfile(OntResource base, boolean required) { - createAllProperties(createProfileClass(base), required); + public void createCompleteProfile(OntResource base, boolean isConcrete, boolean arePropertiesRequired) { + createAllProperties(createProfileClass(base, isConcrete), arePropertiesRequired); } - public void createAllProperties(OntResource clss, boolean required) { - createAllProperties(new ProfileClass(clss, getNamespace()), required); + public void createAllProperties(OntResource clss, boolean arePropertiesRequired) { + createAllProperties(new ProfileClass(clss, getNamespace()), arePropertiesRequired); } }