/*
* Copyright 2006, Dustin Metzgar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using XmiCodeDomLib;
namespace XmiTestApp
{
public partial class MainForm : Form
{
private CodeCompileUnit _CCU = null;
private Mutex _CcuMutex = new Mutex();
public MainForm()
{
InitializeComponent();
foreach (CodeOutputTypes cot in Enum.GetValues(typeof(CodeOutputTypes)))
cboOutputType.Items.Add(cot.ToString());
cboOutputType.SelectedIndex = 0;
}
private void btnBrowse_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "XMI files|*.xmi|XML Files|*.xml|All files|*";
ofd.FilterIndex = 1;
ofd.Multiselect = false;
if (ofd.ShowDialog(this) == DialogResult.OK)
txtXmiFilename.Text = ofd.FileName;
}
private bool VerifyFile()
{
FileInfo fi = new FileInfo(txtXmiFilename.Text);
if (!fi.Exists)
{
MessageBox.Show("File does not exist.");
return false;
}
return true;
}
private void ShowException(Exception exc)
{
StringBuilder sb = new StringBuilder();
Exception exc2 = exc;
while (exc2 != null)
{
sb.Append(exc2.GetType().FullName);
sb.Append(Environment.NewLine);
sb.Append(exc2.Message);
sb.Append(Environment.NewLine);
sb.Append(exc2.StackTrace);
sb.Append(Environment.NewLine);
exc2 = exc2.InnerException;
}
MessageBox.Show(sb.ToString(), "Exception caught during processing.");
}
private void btnGenerate_Click(object sender, EventArgs e)
{
if (VerifyFile())
{
CodeOutputTypes cot = (CodeOutputTypes)Enum.Parse(typeof(CodeOutputTypes), cboOutputType.SelectedItem.ToString());
try
{
XmlTextReader xtr = new XmlTextReader(txtXmiFilename.Text);
Thread t = new Thread(new ParameterizedThreadStart(GenerateCode));
t.IsBackground = true;
t.Start(new object[] { xtr, cot });
}
catch (Exception exc)
{
ShowException(exc);
}
}
}
private void btnHierarchy_Click(object sender, EventArgs e)
{
if (VerifyFile())
{
try
{
XmlTextReader xtr = new XmlTextReader(txtXmiFilename.Text);
Thread t = new Thread(new ParameterizedThreadStart(ShowClassHierarchy));
t.IsBackground = true;
t.Start(xtr);
}
catch (Exception exc)
{
ShowException(exc);
}
}
}
delegate TreeNode AddTreeNode(TreeNodeCollection tnc, string key);
delegate void ShowHideButton(Button btn, bool enabled);
delegate void StartHierarchy();
delegate void EndHierarchy();
delegate void SetupProgressBar(int min, int max, int step);
delegate void ProgressBarStep();
delegate void UpdateStatusBar(string msg);
delegate void ShowCode(string code);
/// <summary>
/// This delegate can be declared statically since it doesn't need access to anything in
/// the object. It must still be a delegate because it has to be called in the main
/// window's thread.
/// </summary>
AddTreeNode _AddTreeNode = delegate(TreeNodeCollection tnc, string key)
{
int i = 0;
for (; i < tnc.Count && tnc[i].Text.CompareTo(key) < 0; i++) {}
return tnc.Insert(i, key, key);
};
/// <summary>
/// This delegate can be declared statically since it doesn't need access to anything in
/// the object. It must still be a delegate because it has to be called in the main
/// window's thread.
/// </summary>
ShowHideButton _ShowHideButton = delegate(Button btn, bool enabled) { btn.Enabled = enabled; };
private void ShowClassHierarchy(object obj)
{
// Create the delegates we need to call on the main thread.
StartHierarchy startH = delegate() { statusBarLabel.Text = "Parsing Hierarchy..."; treeClasses.Cursor = Cursors.WaitCursor; treeClasses.Nodes.Clear(); progressBar.Visible = true; };
EndHierarchy endH = delegate() { /*treeClasses.ExpandAll();*/ treeClasses.Cursor = Cursors.Default; statusBarLabel.Text = "Done"; progressBar.Visible = false; };
SetupProgressBar progBarSetup = delegate(int min, int max, int step) { progressBar.Minimum = min; progressBar.Maximum = max; progressBar.Step = step; };
ProgressBarStep progBarStep = delegate() { progressBar.PerformStep(); };
UpdateStatusBar updStatus = delegate(string msg) { statusBarLabel.Text = msg; };
Invoke(_ShowHideButton, new object[] { btnHierarchy, false });
_CcuMutex.WaitOne();
Invoke(startH);
if (_CCU == null)
{
XmlReader xr = obj as XmlReader;
XmiRoot root = new XmiRoot();
Invoke(updStatus, new object[] { "Parsing XMI..." });
root.Parse(xr);
Invoke(updStatus, new object[] { "Creating CodeDom..." });
_CCU = root.GetCodeCompileUnit();
}
CodeCompileUnit ccu = CopyCcu();
Invoke(updStatus, new object[] { "Parsing Hierarchy..." });
foreach (CodeNamespace cn in ccu.Namespaces)
{
TreeNode nodeNs = Invoke(_AddTreeNode, new object[] { treeClasses.Nodes, cn.Name }) as TreeNode;
Invoke(progBarSetup, new object[] { 1, cn.Types.Count, 1 });
for (int i = cn.Types.Count - 1; i >= 0; i--)
{
CodeTypeDeclaration ctd = cn.Types[i];
if (ctd.BaseTypes.Count == 0)
{
Invoke(_AddTreeNode, new object[] { nodeNs.Nodes, ctd.Name });
cn.Types.RemoveAt(i);
Invoke(progBarStep);
}
}
foreach (TreeNode tn in nodeNs.Nodes)
GetNodeChildren(cn, tn);
}
Invoke(endH);
_CcuMutex.ReleaseMutex();
Invoke(_ShowHideButton, new object[] { btnHierarchy, true });
}
private void GetNodeChildren(CodeNamespace cn, TreeNode tn)
{
for (int i = cn.Types.Count - 1; i >= 0; i--)
{
if (i >= cn.Types.Count)
i = cn.Types.Count - 1;
if (i < 0)
break;
CodeTypeDeclaration ctd = cn.Types[i];
CodeTypeReference ctrFound = null;
foreach (CodeTypeReference ctr in ctd.BaseTypes)
if (ctr.BaseType == tn.Text)
{
ctrFound = ctr;
break;
}
if (ctrFound != null)
{
TreeNode tnChild = Invoke(_AddTreeNode, new object[] { tn.Nodes, ctd.Name }) as TreeNode;
ctd.BaseTypes.Remove(ctrFound);
if (ctd.BaseTypes.Count == 0)
{
cn.Types.RemoveAt(i);
ProgressBarStep progBarStep = delegate() { progressBar.PerformStep(); };
Invoke(progBarStep);
}
GetNodeChildren(cn, tnChild);
}
}
}
private void GenerateCode(object obj)
{
UpdateStatusBar updStatus = delegate(string msg) { statusBarLabel.Text = msg; };
ShowCode showCode = delegate(string code) { txtCode.Text = code; };
XmlReader xr = (obj as Array).GetValue(0) as XmlReader;
CodeOutputTypes cot = (CodeOutputTypes)((obj as Array).GetValue(1));
Invoke(_ShowHideButton, new object[] { btnGenerate, false });
_CcuMutex.WaitOne();
XmiRoot root = new XmiRoot();
if (_CCU == null)
{
Invoke(updStatus, new object[] { "Parsing XMI..." });
root.Parse(xr);
Invoke(updStatus, new object[] { "Creating CodeDom..." });
_CCU = root.GetCodeCompileUnit();
}
Invoke(updStatus, new object[] { "Running Code Generator..." });
string codeString = root.GetParsedCode(cot, _CCU);
Invoke(showCode, new object[] { codeString });
Invoke(updStatus, new object[] { "Done." });
_CcuMutex.ReleaseMutex();
Invoke(_ShowHideButton, new object[] { btnGenerate, true });
}
void TxtXmiFilenameTextChanged(object sender, System.EventArgs e)
{
_CcuMutex.WaitOne();
_CCU = null;
_CcuMutex.ReleaseMutex();
}
/// <summary>
/// Makes a copy of the current CodeCompileUnit. This is used because the methods that
/// show the class hierarchy destroy the CodeCompileUnit.
/// </summary>
/// <returns></returns>
private CodeCompileUnit CopyCcu()
{
try
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, _CCU);
ms.Position = 0L;
CodeCompileUnit ccu = bf.Deserialize(ms) as CodeCompileUnit;
return ccu;
}
catch (Exception exc)
{
throw exc;
}
}
}
}