Generate C# code from Eclipse Modeling Framework (ecore)

I found out a way how to generate C# code through UML diagram finally by using some free tools. Eclipse is a famous and free IDE and very powerful. Currently only Eclipse is offering a free UML diagram tool, I know there are lots more of advance, more powerful one like IBM Rational but you know it is not free.

  1. Draw class diagram in Eclipse.
  2. Create .genmodel file from your .umlclass see tutorial.
  3. Parse ecore namespace and syntax to original xsd compatible.
  4. Generate C# code using Xsd2Code.

Setup Eclipse
Get the incubation copy of Galileo Modeling all-in-one bundle. The file name should be something eclipse-modeling-galileo-SR1-incubation-win32.zip if you are using Windows.

1. Draw class diagram
Here a simple one-to-many aggregation relationship. A room can have many appointments in a day. So be caution when link up the relationship. Otherwise the generator will not correctly. After define upper and lower bound, please specific the parent class as well. And put a name you want which it should become part of a property in the parent class.

2. .genmodel
This file is the important file which can generate xsd for Xsd2Code use later. It use ecore framework to generate so we need to parse the format to we want after that. See tutorial.

output of ecore generated xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:default="http:///default.ecore" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ecore:nsPrefix="default" ecore:package="default" targetNamespace="http:///default.ecore">
  <xsd:import namespace="http://www.eclipse.org/emf/2002/Ecore" schemaLocation="platform:/plugin/org.eclipse.emf.ecore/model/Ecore.xsd"/>
  <xsd:element ecore:ignore="true" name="Room" type="default:Room"/>
  <xsd:element ecore:ignore="true" name="Account" type="default:Account"/>
  <xsd:element ecore:ignore="true" name="Appointment" type="default:Appointment"/>
  <xsd:element ecore:ignore="true" name="Guid" type="default:Guid"/>
  <xsd:element ecore:ignore="true" name="DateTime" type="default:DateTime"/>
  <xsd:element ecore:ignore="true" name="Facility" type="default:Facility"/>
  <xsd:element ecore:ignore="true" name="Employee" type="default:Employee"/>
  <xsd:complexType name="Room">
    <xsd:complexContent>
      <xsd:extension base="default:Account">
        <xsd:sequence>
          <xsd:element ecore:name="Appointments" ecore:ordered="false" ecore:resolveProxies="true" maxOccurs="unbounded" minOccurs="0" name="Appointments" type="default:Appointment"/>
          <xsd:element ecore:name="Facilities" ecore:ordered="false" ecore:resolveProxies="true" maxOccurs="unbounded" name="Facilities" type="default:Facility"/>
        </xsd:sequence>
        <xsd:attribute ecore:name="Location" name="Location" type="ecore:EString" use="required"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <xsd:complexType name="Account">
    <xsd:attribute ecore:name="Id" ecore:unsettable="false" name="Id" type="ecore:EInt" use="required"/>
    <xsd:attribute ecore:name="Alias" name="Alias" type="ecore:EString" use="required"/>
    <xsd:attribute ecore:name="DisplayName" name="DisplayName" type="ecore:EString" use="required"/>
    <xsd:attribute ecore:name="Email" name="Email" type="ecore:EString" use="required"/>
    <xsd:attribute ecore:name="Extension" name="Extension" type="ecore:EString" use="required"/>
    <xsd:attribute ecore:name="Remarks" name="Remarks" type="ecore:EString" use="required"/>
  </xsd:complexType>
  <xsd:complexType name="Appointment">
    <xsd:sequence>
      <xsd:element ecore:name="Id" ecore:resolveProxies="true" name="Id" type="default:Guid"/>
      <xsd:element ecore:name="Start" ecore:resolveProxies="true" name="Start" type="default:DateTime"/>
      <xsd:element ecore:name="End" ecore:resolveProxies="true" name="End" type="default:DateTime"/>
    </xsd:sequence>
    <xsd:attribute ecore:name="ItemId" name="ItemId" type="ecore:EString" use="required"/>
    <xsd:attribute ecore:name="Subject" name="Subject" type="ecore:EString" use="required"/>
  </xsd:complexType>
  .......
</xsd:schema>

3. Parse ecore to original xsd.
You can copy-and-replace manually to purge all the ecore syntax, of course it is a tough job also. So I wrote a snippet console tool to do the job for me.

class Program
    {
        private static List SKIPPED = new List{
            "ecore:ignore=\"true\"","ecore:ignore=\"false\"",
            "ecore:ordered=\"true\"", "ecore:ordered=\"false\"",
            "ecore:unsettable=\"true\"", "ecore:unsettable=\"false\"",
            "ecore:resolveProxies=\"true\"",
            "use=\"required\"",
        };
        private static List SKIPPED_PATTERN = new List{
            "ecore:name=\"[\\w]+\"",
        };

        ///
<summary> /// TODO: Complete list out all Ecore type to Xsd type conversion.
 /// </summary>
        private static Dictionary REPLACEMENT_PAIRS = new Dictionary{
            {"type=\"ecore:EString\"","type=\"xsd:string\""},
            {"type=\"ecore:EInt\"","type=\"xsd:integer\""},
        };

        private static void GetPrefix(string line) {

            string prefix = string.Empty;
            string[] tags= line.Split(new char[]{' '});
            foreach (string tag in tags)
            {
                if (tag.Contains("nsPrefix"))
                {
                    int i = tag.IndexOf('"');
                    prefix = tag.Substring(i + 1, tag.Length - i - 1 - 1) + ":";
                    Console.WriteLine("Prefix is:" + prefix);
                    SKIPPED.Add(prefix);
                }
            }
        }

        private static string Replace(string sender, List source)
        {
            string output = sender;
            foreach (string replace in source)
                output = output.Replace(replace, "");

            return output;
        }
        private static string Replace(string sender, Dictionary source)
        {
            string output = sender;
            foreach (KeyValuePair pair in source)
                output = output.Replace(pair.Key, pair.Value);

            return output;
        }
        private static string ReplaceFormat(string sender, List source)
        {
            string output = sender;
            foreach (string pattern in source)
            {
                Match match = Regex.Match(output, pattern);
                if (match.Success)
                {
                    foreach (Group group in match.Groups)
                        output = output.Replace(group.Value, "");
                }
            }

            return output;
        }

        static void Main(string[] args)
        {
            //looking file to parse
            List files = new List();
            foreach (string a in args)
                files.Add(a);

            if (files.Count == 0)
            {
                DirectoryInfo di = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
                FileInfo[] fi = di.GetFiles();
                foreach (FileInfo f in fi)
                {
                    if (f.Extension.ToLower().EndsWith("xsd"))
                    {
                        Console.WriteLine("Found " + f.Name);
                        files.Add(f.Name);
                    }
                }
            }

            //start parsing...
            foreach (string file in files)
            {
                int i = 0;
                Console.WriteLine("parsing " + file + "...");
                StringBuilder sb = new StringBuilder();

                StreamReader reader = new StreamReader(file);
                string line = string.Empty;
                while ((line = reader.ReadLine()) != null)
                {
                    if (i == 0)
                        sb.AppendLine("<!--?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?-->");
                    else if (i == 1)
                    {
                        sb.AppendLine("");
                        GetPrefix(line);
                    }
                    else if (i == 2)
                    {
                        //do nothing to skip append line 2
                    }
                    else
                    {
                        line = Replace(line, SKIPPED);
                        line = Replace(line, REPLACEMENT_PAIRS);
                        line = ReplaceFormat(line, SKIPPED_PATTERN);
                        //Console.WriteLine(line);
                        sb.AppendLine(line);
                    }

                    i++;
                }//end 1 file

                //write to a new file
                Console.WriteLine(sb.ToString());
                StreamWriter writer = new StreamWriter(file.Replace(".", "2."));
                writer.Write(sb.ToString());
                writer.Flush();
            }//end loops

            //wait for user input to exit
            Console.Read();
        }
    }

after housekeeping

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ms="http://microsoft.com/wsdl/types/" attributeFormDefault="unqualified" elementFormDefault="qualified">
  <xsd:element  name="Room" type="Room"/>
  <xsd:element  name="Account" type="Account"/>
  <xsd:element  name="Appointment" type="Appointment"/>
  <xsd:element  name="Guid" type="Guid"/>
  <xsd:element  name="DateTime" type="DateTime"/>
  <xsd:element  name="Facility" type="Facility"/>
  <xsd:element  name="Employee" type="Employee"/>
  <xsd:complexType name="Room">
    <xsd:complexContent>
      <xsd:extension base="Account">
        <xsd:sequence>
          <xsd:element    maxOccurs="unbounded" minOccurs="0" name="Appointments" type="Appointment"/>
          <xsd:element    maxOccurs="unbounded" name="Facilities" type="Facility"/>
        </xsd:sequence>
        <xsd:attribute  name="Location" type="xsd:string" />
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <xsd:complexType name="Account">
    <xsd:attribute   name="Id" type="xsd:integer" />
    <xsd:attribute  name="Alias" type="xsd:string" />
    <xsd:attribute  name="DisplayName" type="xsd:string" />
    <xsd:attribute  name="Email" type="xsd:string" />
    <xsd:attribute  name="Extension" type="xsd:string" />
    <xsd:attribute  name="Remarks" type="xsd:string" />
  </xsd:complexType>
  <xsd:complexType name="Appointment">
    <xsd:sequence>
      <xsd:element   name="Id" type="Guid"/>
      <xsd:element   name="Start" type="DateTime"/>
      <xsd:element   name="End" type="DateTime"/>
    </xsd:sequence>
    <xsd:attribute  name="ItemId" type="xsd:string" />
    <xsd:attribute  name="Subject" type="xsd:string" />
  </xsd:complexType>
  <xsd:complexType name="Guid"/>
  <xsd:complexType name="DateTime"/>
  <xsd:complexType name="Facility">
    <xsd:attribute   name="Id" type="xsd:integer" />
    <xsd:attribute  name="Name" type="xsd:string" />
    <xsd:attribute  name="Description" type="xsd:string" />
  </xsd:complexType>
  <xsd:complexType name="Employee">
    <xsd:complexContent>
      <xsd:extension base="Account">
        <xsd:attribute  name="FirstName" type="xsd:string" />
        <xsd:attribute  name="LastName" type="xsd:string" />
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

4. You almost done. Now you can generate C# code by using Xsd2Code. Please read more details how to do that at its homepage.

Result:

// ------------------------------------------------------------------------------
//  <auto-generated>
//    Generated by Xsd2Code. Version 3.4.0.38967
//    <NameSpace>default</NameSpace><Collection>List</Collection><codeType>CSharp</codeType><EnableDataBinding>False</EnableDataBinding><EnableLazyLoading>False</EnableLazyLoading><TrackingChangesEnable>False</TrackingChangesEnable><GenTrackingClasses>False</GenTrackingClasses><HidePrivateFieldInIDE>False</HidePrivateFieldInIDE><EnableSummaryComment>False</EnableSummaryComment><VirtualProp>False</VirtualProp><IncludeSerializeMethod>False</IncludeSerializeMethod><UseBaseClass>False</UseBaseClass><GenBaseClass>False</GenBaseClass><GenerateCloneMethod>False</GenerateCloneMethod><GenerateDataContracts>False</GenerateDataContracts><CodeBaseTag>Net20</CodeBaseTag><SerializeMethodName>Serialize</SerializeMethodName><DeserializeMethodName>Deserialize</DeserializeMethodName><SaveToFileMethodName>SaveToFile</SaveToFileMethodName><LoadFromFileMethodName>LoadFromFile</LoadFromFileMethodName><GenerateXMLAttributes>False</GenerateXMLAttributes><EnableEncoding>False</EnableEncoding><AutomaticProperties>False</AutomaticProperties><GenerateShouldSerialize>False</GenerateShouldSerialize><DisableDebug>False</DisableDebug><PropNameSpecified>Default</PropNameSpecified><Encoder>UTF8</Encoder><CustomUsings></CustomUsings><ExcludeIncludedTypes>False</ExcludeIncludedTypes><EnableInitializeFields>True</EnableInitializeFields>
//  </auto-generated>
// ------------------------------------------------------------------------------
namespace default
{
    using System;
    using System.Diagnostics;
    using System.Xml.Serialization;
    using System.Collections;
    using System.Xml.Schema;
    using System.ComponentModel;
    using System.Collections.Generic;


    public partial class Room : Account
    {

        private List appointmentsField;

        private List facilitiesField;

        private string locationField;

        public Room()
        {
            this.facilitiesField = new List();
            this.appointmentsField = new List();
        }

        public List Appointments
        {
            get
            {
                return this.appointmentsField;
            }
            set
            {
                this.appointmentsField = value;
            }
        }

        public List Facilities
        {
            get
            {
                return this.facilitiesField;
            }
            set
            {
                this.facilitiesField = value;
            }
        }

        public string Location
        {
            get
            {
                return this.locationField;
            }
            set
            {
                this.locationField = value;
            }
        }
    }
.....
Advertisements