用C#+XMI技术进行UML模型捕获
2019獨角獸企業重金招聘Python工程師標準>>>
有許多不同的方法可用于捕獲XML模型數據并且把它放到一個數據存儲中,正如你所期望的,包括使用XSLT技術。但是我想使用一種不同的方法-使用C#語言。XSLT是一個用于改變XML文件的好選擇,但是對于更廣闊的不僅僅是轉變數據的應用軟件來說,C#或者另外的象Java這樣的高級語言提供了更大的靈活性。
在本文中,我將展示如何通過使用XMI和C#來剖析一個UML發布圖。首先,我展示一個該方法的簡單的發布圖,所在環境為一個虛構的汽車出租公司并使用 C#來捕獲一些數據。這種數據可以被容易地加到一個數據庫(它已經是ADO調用的相當容易的一部分)上去或者作為更大些的一個基于資產的管理系統的一部分。在每一個示例中,我將逐漸增加原始圖和能夠獲取的信息的復雜性。
一、 示例1-查找方法的名字
我要做的第一件事情是捕獲來自于汽車出租公司的所有方法的名字。共有五個方法,它們是以UML發布圖形描述的(見圖1)。在上一篇文章中,我展示了如何用一個XML文檔來描述一個發布圖。為此,首先要創建該圖,然后把它輸出到一個XMI文件(企業架構)或解壓(MagicDraw)。(在這篇文章中,我再次使用了SparxSystems.com提供的企業架構。)
圖1.五個方法:圖中結點描述了汽車出租公司的方法。每一個結點為一個方法名字。
我將首先開發一簡單main程序-它提示用戶輸入描述一個有效的XMI文檔的XML文檔的名字。一旦輸入文件名字,它就被傳遞到一個分析XMI文檔的對象并打印輸出方法名字。
using System;
using System.Text;
namespace XMI_1{
public class ConsoleUtils{
public static string ReadString(string msg) {
Console.Write(msg); return System.Console.ReadLine();
}
public static void WriteString(string msg)
{ System.Console.WriteLine(msg); }
public static void Main(string[] args) {
string name = ReadString("Please enter the XMI filename : ");
NodeParse np = new NodeParse(name);
System.Console.ReadLine();
}
}
}
下一步是分析XMI文檔。為此,我必須創建一個XMLTextReader的實例并循環操作直到沒有結點為止。XMLNodeType.Element 檢查每一個XML元素并且在元素是一個結點的情況下把它打印輸出到控制臺。該元素名字資格為"UML:Node",然而通過使用 _readXMI.LocalName只有元素名字的"Node"被讀取。詳細代碼參見列表1。
二、 示例2-增加模板
第二個示例重構了第一個示例來捕獲方法的名字和"pc server"模板-這是一個用來決定每個結點是一個方法的UML方法。圖3顯示增加了模板后的方法的發布圖。
第一步是修改前面的代碼以識別新的模板來操縱AddNode()方法。
圖3.增加了模板之后:每個模板描述了結點的類型-不管它是一個PC服務器,PC客戶端或者是另外一些描述該結點的目標。
private void AddNode(XmlTextReader p_readXMI)
{
string nodeID;
string nodeName;
string stereotypeName;
nodeID = p_readXMI.GetAttribute("name");
nodeName = p_readXMI.GetAttribute("xmi.id");
Console.Write(nodeID);
Console.Write(" -> ");
Console.WriteLine(nodeName);
while (p_readXMI.Read() && (p_readXMI.NodeType == XmlNodeType.Element ||?
p_readXMI.NodeType== XmlNodeType.Whitespace))
{
switch (p_readXMI.LocalName)
{
case "ModelElement.stereotype":
while (p_readXMI.Read() && (p_readXMI.NodeType == XmlNodeType.Element ||?
p_readXMI.NodeType == XmlNodeType.Whitespace))
{
if (p_readXMI.LocalName == "Stereotype")
{
stereotypeName = p_readXMI.GetAttribute("name");
Console.WriteLine("Stereotype = " + stereotypeName);
}
}
break;
}
}
}
特別要注意的是一個idiom(對某個語言特有的一個低級別的模式)-我用來查找在另外一些元素內部的XML元素。我在p_readXMI.Read循 環中增加了代碼來檢查元素或者空格(在XML文檔中,這是些空格字符)。只有每個元素結點在XML文檔中適當的層次上處理時,這個idiom才工作。
用這種方式對元素進行封裝,結果不很理想。例如,在下面的UML中使用idiom代碼將產生所不希望的結果:
?
| <UML:Node name="Leasing"> <UML:TagName name="Test"> </UML:Node name="SmallLeasing"> </UML:TagName> </UML:Node> |
?
|
|
第一個元素被作為一個結點處理,第二個元素被忽略并在初始循環中退出,而第三個元素被作為在同一級上的第一個正常結點處理。為了修正這個問題,你可以使 用XMLNodetype.Endelement來檢查元素的結束標簽。在本文中,這不是個問題而保存額外的代碼要求檢查結尾元素。
XMI中的模板被封裝在結點元素中。該示例中的代碼檢查名字為"ModelElement.Stereotype"的元素并且使用剛才討論的相同的idiom來處理一個"模板"類型的封裝元素,然后把它打印輸出到屏幕上(見圖4)。(列表3列出了示例2的完全的源碼。)
三、 示例3-添加硬件信息
從資產管理的角度來看,捕獲方法的名字和它們的模板并沒有多大用處。當然,另一方面,如果你的圖包含了描述正在使用的硬件信息也可能是非常有用的:例如,基于UML發布圖把你所有的硬件保存到一個數據庫將能夠使你跟蹤方法、客戶及其如何進行彼此聯系的。
在UML中,硬件結點都貼有用來識別它們屬性的"標簽"。每一個標簽都是在建模工具(這里是企業架構)中產生而且由建模器所定義(或者有時保存成一個 UML剖面文件)。在本示例中,我為CPU、磁盤大小、內存大小、目的、注意事項以及賣主等創建了標簽(見圖5)。每個標簽具有一個值-或者被賦予一個基 本類型(字符串,整型……)或者從一個可用值列表中選擇其一。無論如何,保持與所用值的一致性是很重要的。如果你把"GHT"用于CPU以描述 "gigahertz hyper-threaded",那么對每個CPU標簽,你都要使用相同的約定。
?
|
|
新的重構的代碼與以前的一樣,但是增加了讀取標簽的代碼。在此,XMI并沒有如你所盼的那樣封裝結點內的標簽。作為代替,每個屬性是一個 "TaggedValue"元素-它通過使用"modelElement"屬性來參考引用屬性的結點標識符(XML.id)。這樣做的困難在于結點元素必 須在標簽元素之前被讀取,并且每個結點元素必須被保存-為使標簽元素依附于其上。
在XML中,存在兩種讀取文檔的方法。第一種是讀取 完整的文檔并把它以一棵樹保存到內存中-這里每個元素是從根元素開始構建的層次結構的一部分。這就是DOM模型,是較佳的適用于小型文檔的方法。第二種方 法是,讀取文檔時,每次分析一個元素。SAX就是這種方法的一個示例并且它被當作推模型,因為由它分析文檔并返回分析后的文檔(推它)而不需要提示。
另外一種方法是微軟的XMLReaders(XMLTextReader派生于它)-它是一個拉模型,因為當下一個元素被分析時,控制掌握在程序中。我 在此使用的XML文檔很小,但是我分析過的一些文件是超過了500,000行的文本文件,這導致我求助于XMLReader方法。這種方法的一個不足是要 求結點應出現在標簽元素之前。
為了保存結點我需要使用一個鍵/值容器。最易于使用的是Hashtable。在重構主程序中(見下),我使用了Hashtable中的枚舉能力以及用IDictionary枚舉器來打印方法結點。
?
| public static void Main(string[] args){ Hashtable mainHash; IDictionaryEnumerator ienum; Node tNode; string name = ReadString("Please enter the XMI filename : "); NodeParse np = new NodeParse(name); mainHash = np.getNodes(); ienum = mainHash.GetEnumerator(); while (ienum.MoveNext()) { tNode = (Node)ienum.Value; System.Console.WriteLine("Node ="+tNode.name+" CPU= "+tNode.CPU); } System.Console.ReadLine(); } |
我重構了nodeParse對象以用于檢查元素"TaggedValue"并且調用AddAttributeNode-它負責在哈希表中查找正確的結點并且通過一個case語句把標簽添加到該結點上。相應的類NodeParse顯示于列表4中。
這個結點類僅僅是一個存儲狀態的對象。每一個標簽都有它自己的屬性。注意,這個結點類的構造器要求該對象必須用服務器名字初始化。?
?
| using System; using System.Text; namespace XMI_1 { class Node{ string _name,_id = "",_CPU,_MemorySize,_DiskSize,_Note,_Purpose, _Stereotype,Vendor; public Node(string p_id) {this._name = p_id;} public string id {get { return _id;} public string name{get {return _name;} set {_name = value;}} public string CPU {get { return _CPU;} set {_CPU = value;}} public string MemorySize{get {return _MemorySize;} set {_MemorySize = value;}} public string DiskSize {get {return _DiskSize;}set {_DiskSize = value;}} public string Notes{get {return _Notes;}set {_Notes = value;}} public string Purpose{get {return _Purpose;} set {_Purpose = value;}} public string Stereotype {get {return _Stereotype;} set {_Stereotype = value;}} public string Vendor {get {return _Vendor;} set {_Vendor = value;}} } } |
我并沒有提供示例3和輸出結果,因為它幾乎和示例4的完全相同。(列表5列出了示例3的完整的源代碼。)
四、 示例4-添加繼承
在最后的示例中,我從一個Node類繼承了結點實例。以前,我們每次只分析一個結點并對其進行遍歷。Node實例描述了存在于UML結點和硬件之間的關系。Nodes給了我們一種從實例中抽象出公共元素的方法。
為此,有兩種不同的方法。第一種方法是使用Nodes來描述一個通用硬件平臺。可以設想這樣的情形:我為一家公司工作,該公司想針對它們所有的方法(多 么奇怪!)訂購相同的計算機配置。可以用一個結點來描述典型的計算機配置,然后該結點又會有多個結點實例。這將節省大量的輸入時間!另外一種方法是在軟件 架構師通知基礎構件小組怎樣分發組件的情形。該結點用針對于每個層的本地名字來描述不同的層。至于這些如何映射到實際的硬件是由結點實例所決定的而且由基 礎構件小組所創建。?
在該示例中(見圖6),我展示了一個通用結點-"Fleet Management",它具有可以添加到它上面的組件。在一個多層系統中,Fleet Management是由軟件架構師來定義成一個分離的層。為了說明問題,我可能還要應用"Purpose"標簽。我有兩個結點實例,"Trucks"和 "Persons",它們由基礎構件小組來定義以把該層分成兩個方法。既然"Trucks"和"Persons"是完全不同的兩個域,那么架構師介入其中 并分解之是十分安全的。方法名字上還標記有":Fleet Management",以指明它們是在Fleet Management結點中實現的。
?
|
|
在這個示例中,沒有用于繼承的標簽,但是可能在另外的圖上存在一些其它關系-它們會連接到該結點實例上。
我又一次重構了main程序以打印出所有的,包括每一個結點實例派生的(tNode.classname)結點標簽,還有結點的ID(tNode.classifier)。
?
| public static void Main(string[] args) { Hashtable mainHash; IDictionaryEnumerator ienum; Node tNode; string name = ReadString("Please enter the XMI filename : "); NodeParse np = new NodeParse(name); mainHash = np.getNodes(); ienum = mainHash.GetEnumerator(); while (ienum.MoveNext()){ tNode = (Node)ienum.Value; System.Console.WriteLine("Node = " + tNode.name); //Add "if (tNode. != null)" for each Writeline()? System.Console.WriteLine(" CPU = " + tNode.CPU); System.Console.WriteLine(" DiskSize = " + tNode.DiskSize); System.Console.WriteLine(" MemorySize = " + tNode.MemorySize); System.Console.WriteLine(" Purpose = " + tNode.Purpose); System.Console.WriteLine(" Notes = " + tNode.Notes); System.Console.WriteLine("Node refers to = " + tNode.classname); System.Console.WriteLine("Node addr is " + tNode.classifier); System.Console.WriteLine(); } System.Console.ReadLine(); } |
類名和類標志符指明該結點實例繼承自哪些結點。在XMI中,這是被象一個模板(就象一個封裝在結點元素中的元素)一樣描述的。
現在我將使用一個開關語句來檢查"ModelElement.taggedValue",而代之以ModelElement.Stereotype,查 找"TaggedValue"元素(存在類名和類標志符上皆獨立的元素),并把它們添加到結點對象上。在此,我再次使用了分析idiom。這些代碼可以在 列表3中找到。
圖7.Trucks和Persons:示例4的最后輸出,Trucks和Persons各有一個它們可以參考的結點并且每個結點都有一個唯一的ID。
圖7顯示示例4最后的輸出。
五、 總結
?
在本文中,我討論了可以從圖中讀取硬件資產的方法。還討論了讀取名字、模板、屬性以及讀取結點之間及結點實例(繼承)之間的關系。盡管我沒有給出把這種信息放入數據庫中的顯式代碼,我將在后面的文章中討論這些技術-當然不止是添加信息到數據庫中。
?
登錄樂搏學院官網http://www.learnbo.com/
或關注我們的官方微博微信,還有更多驚喜哦~
?
本文出自 “青峰” 博客,轉載請與作者聯系!
轉載于:https://my.oschina.net/learnbo/blog/829239
總結
以上是生活随笔為你收集整理的用C#+XMI技术进行UML模型捕获的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在eclipse中安装propertie
- 下一篇: 在C#代码中应用Log4Net系列教程(