以 XML 或 GZip 壓縮後的 XML 作為文件保存格式,方便於其他程式語言分析
雖然 Dia 有多種圖表製作,但在下用最多的功能暫時只是資料庫圖表,因此本文亦主要以資料庫圖表為分析目標
Dia 的資料庫圖表 XML 其實相當直觀,以下是 Dia 的資料庫圖表 XML 比較重點的結構
<?xml version="1.0" encoding="UTF-8"?> <dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/"> ... <dia:object type="Database - Table" version="0" id="O0"> ... <dia:attribute name="name"> <dia:string>#table name#</dia:string> </dia:attribute> <dia:attribute name="comment"> <dia:string>#table comment#</dia:string> </dia:attribute> ... <dia:attribute name="attributes"> <dia:composite type="table_attribute"> <dia:attribute name="name"> <dia:string>#attribute name#</dia:string> </dia:attribute> <dia:attribute name="type"> <dia:string>#attribute type#</dia:string> </dia:attribute> <dia:attribute name="comment"> <dia:string>#attribute comment#</dia:string> </dia:attribute> <dia:attribute name="primary_key"> <dia:boolean val="true"/> </dia:attribute> <dia:attribute name="nullable"> <dia:boolean val="false"/> </dia:attribute> <dia:attribute name="unique"> <dia:boolean val="true"/> </dia:attribute> </dia:composite> ... </dia:attribute> ... </dia:object> ... <dia:object type="Database - Reference" version="0" id="O3"> ... <dia:connections> <dia:connection handle="0" to="O1" connection="12"/> <dia:connection handle="1" to="O0" connection="43"/> </dia:connections> </dia:object> ... </dia:diagram>例子中的 ... 為其他 XML 資料,但大部分都是顯示效果設定,對於資料庫的結構沒有關係,因此省略掉
結構上
以 dia:object type="Database - Table" 為資料表
table 的資料中主要包括 3個 主要資料,分別為:
<dia:attribute name="name"> 為資料表的名稱,以 <dia:string>#table name#</dia:string> 為名稱
<dia:attribute name="comment"> 為資料表的備註,以 <dia:string>#table comment#</dia:string> 為備註
<dia:attribute name="attributes"> 為資料表的屬性列表
在 <dia:attribute name="attributes"> 下會有 <dia:composite type="table_attribute"> 為資料表的屬性
<dia:composite type="table_attribute"> 包括 6個 主要資料,分別為:
<dia:attribute name="name"> 為屬性的名稱,以 <dia:string>#attribute name#</dia:string> 為名稱
<dia:attribute name="type"> 為屬性的類型,以 <dia:string>#attribute type#</dia:string> 為類型
<dia:attribute name="comment"> 為屬性的備註,以 <dia:string>#attribute comment#</dia:string> 為備註
<dia:attribute name="primary_key"> 為屬性是否主鍵,以 <dia:boolean val="true"> 為是或否
<dia:attribute name="nullable"> 為屬性是否能設定為 null,以 <dia:boolean val="true"> 為是或否
<dia:attribute name="unique"> 為屬性是否唯一鍵,以 <dia:boolean val="true"> 為是或否
<dia:string>#table name#</dia:string> 為文字資料並是 ## 為起首與結尾符號
<dia:boolean val="true"> 為是與否資料 val 為 true 表示「是」,val 為 false 表示「否」
以 dia:object type="Database - Reference" 為 table 與 table 之間的關係
<dia:connection handle="0" to="O1" connection="12"/>
handle 為 0 表示的起首
to 為相關資料表的 id
connection 為圖表上定位點的代號
0 至 4 為上線左至右的定位點,共 5 點
5, 6 為資料表名稱左右兩側的定位點,共 2 點
7 至 11 為下線左至右的定位點,共 5 點
由 12, 13 開始是圖表的第一個屬性, 14, 15 為第二個屬性,如此類推……
確定 XML 結構後便可以利用程式語言來將 Dia 轉換成 MySQL 格式
不過在下在編寫此程式前,嘗試過使用 dia2code 將 dia 轉成 sql 不過最後只能輸出 segmentation fault
而且這個 bug 好像到現在都沒有修正……
事不宜遲,開始編制屬於大家的程式
基於 Dia 是 XML ,可以使用由 Java 提供的 DOM 來分析 Dia
try{ DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); builder.setErrorHandler(null); Element element = builder.parse(file).getDocumentElement(); } catch (Exception ex){ ex.printStackTrace(); }
先獲取所有 dia:object 再只收取 type 為 Database - Table 的 element
留意需要獲取 dia:object 的 id 讓往後的 dia:connection 可以辨認 table 資料
NodeList diaObjects = element.getElementsByTagName("dia:object"); for (int i = 0; i < diaObjects.getLength(); i++){ Element diaObject = (Element) (diaObjects.item(i)); if (diaObject.getAttribute("type").equals("Database - Table")){ String id = diaObject.getAttribute("id"); } }由於 DOM 的 getElementsByTagName 會將由該 element 之下所有符合目標的 element 都會收取
使用 getElementsByTagName 後所收取的 element
必須以 getParentNode 來確定 element 是否為原來的 element 的 child 否則次序會出錯
或
可以使用 getChildNodes 只收取當前 element 的所有 child node
再收取 getNodeType 為 ELEMENT_NODE 的 element
當然還可以使用 getNextSibling 不過在下沒有實作此方法
前者的方式,如果 Dia 的結構非常巨型,有很多 dia:attribute 的 element 話速度會非常慢
NodeList diaAttributes = diaObject.getElementsByTagName("dia:attribute"); for (int j = 0; j < diaAttributes.getLength(); j++){ Element tableAttribute = (Element) (diaAttributes.item(j)); if (tableAttribute.getParentNode() == diaObject){ } }
NodeList diaAttributes = diaObject.getChildNodes(); for (int j = 0; j < diaAttributes.getLength(); j++){ Node diaAttribute = diaAttributes.item(j); if (diaAttribute.getNodeType() == Node.ELEMENT_NODE){ Element tableAttribute = (Element) diaAttribute; } }透過屬性 name 分拆出 name, comment, attribute
String name = tableAttribute.getAttribute("name").toLowerCase(); if (name.equals("name")){ String data = tableAttribute.getElementsByTagName("dia:string").item(0).getTextContent(); data = data.substring(1, data.length() - 1); } else if (name.equals("comment")){ String data = tableAttribute.getElementsByTagName("dia:string").item(0).getTextContent(); table.comment = data.substring(1, data.length() - 1); } else if (name.equals("attributes")){ }
分拆到 attribute 時再次利用 getElementsByTagName 收取所有 dia:composite 的 element
聚焦到 dia:composite 後再用 getElementsByTagName 收取所有 dia:attribute 的 element
不過由於 dia:composite 之下沒有其他 dia:attribute 所以可以直接使用 getElementsByTagName 即可
之後便可以分拆到 name, type, comment, primary_key, nullable, unique
NodeList diaComposites = element.getElementsByTagName("dia:composite"); for (int i = 0; i < diaComposites.getLength(); i++){ NodeList diaAttributes = ((Element) (diaComposites.item(i))).getElementsByTagName("dia:attribute"); for (int j = 0; j < diaAttributes.getLength(); j++){ Element diaAttribute = (Element) (diaAttributes.item(j)); String name = diaAttribute.getAttribute("name").toLowerCase(); if (name.equals("name")){ String data = diaAttribute.getElementsByTagName("dia:string").item(0).getTextContent(); data = data.substring(1, data.length() - 1); } else if (name.equals("type")){ String data = diaAttribute.getElementsByTagName("dia:string").item(0).getTextContent(); data = data.substring(1, data.length() - 1); } else if (name.equals("comment")){ String data = diaAttribute.getElementsByTagName("dia:string").item(0).getTextContent(); data = data.substring(1, data.length() - 1); } else if (name.equals("primary_key")){ boolean data = ((Element) (diaAttribute.getElementsByTagName("dia:boolean").item(0))).getAttribute("val").toLowerCase().equals("true"); } else if (name.equals("nullable")){ boolean data = ((Element) (diaAttribute.getElementsByTagName("dia:boolean").item(0))).getAttribute("val").toLowerCase().equals("true"); } else if (name.equals("unique")){ boolean data = ((Element) (diaAttribute.getElementsByTagName("dia:boolean").item(0))).getAttribute("val").toLowerCase().equals("true"); } } }再次由最頂層的 element 獲取所有 dia:object 再只收取 type 為 Database - Reference 的 element
聚焦到 dia:object 後以 getElementsByTagName 獲得 dia:connection
合理情況下必定有 2個 dia:connection
第1個 dia:connection 是終點,第2個 dia:connection 是起點
獲得 2個 dia:connection 後將 屬性 to 及 屬性 connection 的資料收取,並連結回指定的 2個 table
NodeList diaObjects = element.getElementsByTagName("dia:object"); for (int i = 0; i < diaObjects.getLength(); i++){ Element diaObject = (Element) (diaObjects.item(i)); if (diaObject.getAttribute("type").equals("Database - Reference")){ NodeList connections = diaObject.getElementsByTagName("dia:connection"); Element connectionFrom = (Element) (connections.item(1)); Element connectionTo = (Element) (connections.item(0)); int indexFrom = Integer.parseInt(connectionFrom.getAttribute("connection")) / 2 - 6; int indexTo = Integer.parseInt(connectionTo.getAttribute("connection")) / 2 - 6; } }
最後就便需要整理這些資料成為 MySQL 的格式便完成
在下將這個專案上載至 https://bitbucket.org/hkgoldenmra/dia2mysql
或使用 git
git clone https://hkgoldenmra@bitbucket.org/hkgoldenmra/dia2mysql.git專案定義了 Database, Table, Attribute 類別方便整合資料
歡迎參與
沒有留言 :
張貼留言