Click here to Skip to main content
Click here to Skip to main content

Xmi CodeDom Library, Part 1

By , 26 May 2008
 
Sample image

Introduction

XMI (XML Metadata Interchange) is a standard way of describing a UML class diagram. Most modern UML modeling tools now feature XMI support. In this article, I will demonstrate how to use the XMI-CodeDom Library I have created.

Thanks

First, I want to thank those who helped me build this library. It is not yet finished, but it has been an enjoyable hobby. First, I would like to thank Diana Mohan who provided me with a rather large XMI document (~8 megs) and some good feedback on the early stages of development. The large document helped me track down a lot of performance problems. Next, I'd like to thank the guys over at ArgoUML for making a free UML editor with import/export of XMI. Finally, thanks goes to www.zvon.org for their very helpful (although incomplete) XMI reference.

Example Windows Application

Included with the source code is an example application that allows the user to read an XMI document (pictured above). A CodeDom graph is created from that XMI which can then be used to determine the class hierarchy or generate code in a number of languages. This application really doesn't need much explanation. One thing that has to be recognized is that CodeDom allows multiple inheritance and so does XMI, but that doesn't mean it will compile in .NET.

Notes on XMI

There are two XMI versions covered by this library: 1.0 and 1.2. The two versions are substantially different and made for an interesting challenge. Here is an example of a small model done with version 1.0:

<XMI xmi.version="1.0">
  <XMI.header>
    <XMI.documentation>
      <XMI.exporter>Novosoft UML Library</XMI.exporter>
      <XMI.exporterVersion>0.4.20</XMI.exporterVersion>
    </XMI.documentation>
    <XMI.metamodel xmi.name="UML" xmi.version="1.3"/>
  </XMI.header>
  <XMI.content>
    <Model_Management.Model xmi.id="xmi.1" 
      xmi.uuid="-64--88-1-3--a14cac0:10ab9466697:-8000">
      <Foundation.Core.ModelElement.name>untitledModel</Foundation.Core.ModelElement.name>
      <Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
      <Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
      <Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
      <Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
      <Foundation.Core.Namespace.ownedElement>
        <Foundation.Core.Class xmi.id="xmi.2" 
          xmi.uuid="-64--88-1-3--a14cac0:10ab9466697:-7ffe">
          <Foundation.Core.ModelElement.name>ClassA</Foundation.Core.ModelElement.name>
          <Foundation.Core.ModelElement.visibility xmi.value="public"/>
          <Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
          <Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
          <Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
          <Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
          <Foundation.Core.Class.isActive xmi.value="false"/>
          <Foundation.Core.ModelElement.namespace>
            <Foundation.Core.Namespace xmi.idref="xmi.1"/>
          </Foundation.Core.ModelElement.namespace>
        </Foundation.Core.Class>
      </Foundation.Core.Namespace.ownedElement>
    </Model_Management.Model>
  </XMI.content>
</XMI>

And here is the exact same model, only done with version 1.2:

<XMI xmi.version = '1.2' xmlns:UML = 'org.omg.xmi.namespace.UML'>
  <XMI.header>
    <XMI.documentation>
      <XMI.exporter>ArgoUML (using Netbeans XMI Writer version 1.0)</XMI.exporter>
      <XMI.exporterVersion>0.20.x</XMI.exporterVersion>
    </XMI.documentation>
    <XMI.metamodel xmi.name="UML" xmi.version="1.4"/>
  </XMI.header>
  <XMI.content>
    <UML:Model xmi.id = '.:0000000000000720' 
       name = 'untitledModel' isSpecification = 'false'
      isRoot = 'false' isLeaf = 'false' isAbstract = 'false'>
      <UML:Namespace.ownedElement>
        <UML:Class xmi.id = '.:0000000000000721' name = 'ClassA' visibility = 'public'
          isSpecification = 'false' isRoot = 'false' 
          isLeaf = 'false' isAbstract = 'false'
          isActive = 'false'/>
      </UML:Namespace.ownedElement>
    </UML:Model>
  </XMI.content>
</XMI>

There is definitely a difference. A lot more of the data is stored in attributes instead of elements, which significantly reduces file sizes. Coincidentally, that 8 meg XMI file I was talking about earlier is in version 1.2, so there's a lot of data in it.

At first, XMI doesn't seem all that bad. The namespaces and classes look pretty straightforward. How hard could it be? That's what I thought until I really got into it. Once you start dealing with multiplicities, associations, generalizations, abstractions, and specifications you realize that there is a lot going on in XMI. I could go into a lot of the implementation details of XMI, but it seems that not many people are really interested in that part. So I won't bore anyone with the details.

Understanding the Code

To handle the different versions and to make parsing the documents fast, easy-to-program, and use a small memory footprint, I chose an interesting design for the library. I'll try to write out how I arrived at this particular solution.

XMI Interfaces?

If you take a look at the version 1.0 XML, you can see some similarities between a class and a namespace. Both of them have child elements starting with "Foundation.Core.ModelElement" and "Foundation.Core.GeneralizableElement". The class definition has an added "Foundation.Core.Class" child element. This led me to believe that perhaps ModelElement and GeneralizableElement could be considered interfaces which are implemented by both Model and Class. Properties of these classes or interfaces can map into either an attribute or a specifically named child element.

Version 1.2 has a very different style, but I think the same rules still apply. A similar pattern reveals itself as you work with more complex documents. The placing of the attributes makes parsing the two versions quite a bit different.

Reflection

My first approach was to grab an XmlReader and just start going through the document element by element. I divided each important piece, like namespace and class, into its own class that was responsible for handling the parsing. This was going quite well, but was quickly becoming very tedious.

The logical next step was to encapsulate the code that gets repeated in each method. Unfortunately, this code was just different enough to give me a headache, so I turned to using reflection. The XMI CodeDom Library code included with this article uses that reflection-based parsing.

By using reflection, I could pull out all the actual XML reading code and just leave the classes as data containers. This ended up greatly improving the speed of development and cut out a lot of tedium. Here's an example of one such data container:

[XmiParser("UML:Association")]
public class XmiAssociation : XmiBaseClass, IModelElement, IGeneralizableElement
{
    private List<XmiAssociationEnd> _Connection = new List<XmiAssociationEnd>();
    
    public List<XmiAssociationEnd> Connection
    {
        get { return _Connection; }
    }
        
        #region IModelElement Members

        private string _Name = string.Empty;
        private bool _IsSpecification;
        private string _Visibility = string.Empty;

        [XmiAttribute]
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        [XmiAttribute]
        public bool IsSpecification
        {
            get { return _IsSpecification; }
            set { _IsSpecification = value; }
        }

        [XmiAttribute]
        public string Visibility
        {
            get { return _Visibility; }
            set { _Visibility = value; }
        }

        #endregion

        #region IGeneralizableElement Members

        private bool _IsRoot;
        private bool _IsLeaf;
        private bool _IsAbstract;

        [XmiAttribute]
        public bool IsRoot
        {
            get { return _IsRoot; }
            set { _IsRoot = value; }
        }

        [XmiAttribute]
        public bool IsLeaf
        {
            get { return _IsLeaf; }
            set { _IsLeaf = value; }
        }

        [XmiAttribute]
        public bool IsAbstract
        {
            get { return _IsAbstract; }
            set { _IsAbstract = value; }
        }

        #endregion        
}

Notice that there is an attribute on the class that tells the parser that this class holds data for "UML:Association". The parser will look at all the classes in a given namespace to find those with XmiParserAttribute attributes. When an element is encountered while parsing the XMI, the parser looks at its table of data containers to see if there's a match. It then uses reflection to fill in the properties of that container. The XmiAttributeAttribute marks properties that appear as attributes instead of as child elements.

Also in the above code, you can see that there is a list of XmiAssociationEnd objects. When the parser sees this, it will look in the "connection" element and attempt to parse the children of that element as XmiAssociationEnd objects. Those parsed objects are then added to the list.

Using the API

Using the library is pretty simple. All you have to do is use the XmiRoot class and provide it with a XmlReader. For example, let's grab a XmlTextReader on a file:

XmlTextReader xtr = new XmlTextReader("MyDiagram.xmi");

Now create an instance of XmiRoot and invoke its Parse method:

XmiRoot root = new XmiRoot();
root.Parse(xtr);

Now, the XmiRoot object contains a representation of the XMI model and can convert that into a CodeCompileUnit:

CodeCompileUnit ccu = root.GetCodeCompileUnit();

Or you can directly create code:

string s = root.GetParsedCode(CodeOutputTypes.CSharp);

As you can see, the API is straightforward. Parsing errors are simply bubbled up as exceptions. If there are nodes that the parser does not recognize, it will skip over them.

Summary

The way XMI is designed lends itself to make it easy to read using the method I've described above. With the parsing code written as it is, it will be easy to expand on the capabilities of the system. Reading other XMI objects is as simple as adding a new class to the project.

Take a look at part 2 to see how I improved upon the Reflection-based parsing by using CodeDom to dynamically create types.

Xmi CodeDom Library, Part 2 - Using dynamic types to increase performance[^]

History

  • 0.1 : 2006-05-23 : Initial version
    XMI versions 1.0 and 1.2 are handled for one namespace with classes, data types, generalizations, associations, multiplicities, class attributes, and class operations. The system uses a Reflection-based parser.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dustin Metzgar
Software Developer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionRunnig the project [modified]memberArash I29 Mar '11 - 4:42 
Hi to all,
 
I read the article and downloaded the attached folder; But I could not find any executable file or a main source which help me run the project. In summery I could not use the project. Please guide me to run and use the program.
 
Thank you,

modified on Tuesday, March 29, 2011 10:53 AM

General[Message Removed]membernompel6 Oct '08 - 9:15 
Spam message removed
GeneralVery SweetmemberJonathan C Dickinson29 Jul '08 - 1:15 
I love it. People need to use architecting more these days.
 
He who asks a question is a fool for five minutes. He who does not ask a question remains a fool forever. [Chineese Proverb]
 
Jonathan C Dickinson (C# Software Engineer)

Generalxmi State machinememberLeblanc Meneses27 May '08 - 19:07 
are you planning on writing up anything on xmi and state machine code generation?
 
this is one area i'm particularly interested in.
 

http://www.w3.org/TR/2005/WD-scxml-20050705/
I've been debating on whether to base my code generation on scxml or xmi state machine docs.
 
[quote]
A number of other XML-based state machine notations have been developed, but none serves the same purpose as SCXML. XMI [UML XMI] is a notation developed for representing UML diagrams, including Harel State charts. However it is intended as a machine interchange format and is not readily authorable by humans. ebXML [OASIS ebXML] is a language for business process specification intended to support B2B e-commerce applications. It contains a state machine language that is in some ways similar to the one presented here, but its syntax and semantics are closely tied to its intended use in e-commerce. It is therefore not suitable as a general-purpose state machine language. XTND [XTND], also called XML Transition Network Definition, is a notation for simple finite state machines but lacks Harel's notions of hierarchical and parallel states and are thus not suitable for a general-purpose state machine that is semantically equivalent to Harel statecharts.
[/quote]
 
--------------------------
 
although this is a styling preference i would change:
string s = root.GetParsedCode(CodeOutputTypes.CSharp);
 
CSharpVisitor visitor = new CSharpVisitor();
string s = root.Render(visitor);
 

 
this way you separate the tree from the presentation...
 

another way is to use dependency injection.. basically instantiates the render off a config file... reducing the statement furthur to:
string s = root.GetParsedCode(); // requires a app.config file to specify a provider="Company.CSharpVisitor, Company" == type, assembly
GeneralRe: xmi State machinememberDustin Metzgar5 Jun '08 - 18:48 
Sorry for taking so long to reply. I was trying to get some time to check out XMI state machines a little bit. It sounded familiar, because I believe I had looked at the idea before. One of the problems I had with it was the lack of interest and support for XMI in general. But also I haven't yet seen an editor for XMI state machines. There are a few articles here on CP that build state machines with certain tools, but don't use XMI. The idea sounds interesting at first because you could potentially generate more than just stubs and have actual running code. But at the same time, I wonder if it wouldn't be better to just use something like WF to draw a state machine.
 
I'm really happy you took the time to write me about this article and I'm sorry I couldn't be of more help. Thanks for the comment on the UI too. My UI does feel pretty kludgey.
 
“Ooh... A lesson in not changing history from Mr. I'm-my-own-grandpa” - Prof. Farnsworth

GeneralRe: xmi State machinememberthomy1 Dec '09 - 1:47 
I use ArgoUML (http://argouml.tigris.org/[^]) to create state machines using state diagrams and create code with some xslt. The xslt file differs depending which state machine library you use. I use StaMa from codeplex.
GeneralThis is an Important ArticlememberDavid7773 Apr '06 - 7:12 

Thank you for sharing Dustin - you got my 5. Please keep us uptodate with your progress.
 
Thanks again,
David Roh

GeneralRe: This is an Important ArticlememberLeblanc Meneses27 May '08 - 18:53 
I agree xmi is the way to do code generation.
 
model based engineering is being used at my work with rhapsody and enterprise architect.
GeneralUniversal XMI supportmemberSébastien Ros11 Feb '06 - 21:41 
As you noticed, each UML editor generates slightly different XMI files for the same model. The issue is that you can't apply a specific logic that would be compatible with any UML editor.
At my company we decided to make a layer between the importation of the XMI files and its use to standardize it in a proprietary format so as to anyone can use the same logic with any UML vendor.
If think it's an important part you should consider when willing to use the XMI format.
 

 
Sincerely,
Sébastien Ros (s.ros_evaluant.com)

GeneralRe: Universal XMI supportmemberScott Nieman15 Feb '06 - 3:22 
I believe that you may be seeing XMI that is either based on the M3 level (MOF meta-metamodel) or the M2 UML meta-model schema. They are different. Most tools do not tell you how they are storing this, and most use the UML meta-model schema. The MOF approach is far better from the point of universal mapping to languages, while the UML approach preserves the semantics of UML.
 
What I have a problem with in UML 2.0 is the inability to model hierarchical state machines without name referencing between action states and use class realizations; i.e., you cannot use IDREF. Class diagramming is 'cute' and serves most of the data structure related problems, but the 'fun' lies in state machines to modeling the business logic and behavior of the systems, and have real 'useful' code generation capabilities. Without a hierarchical approach, you have to be careful how you name 'things'.
 
cheers,
Scott
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 27 May 2008
Article Copyright 2006 by Dustin Metzgar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid