使用Java Swing 創(chuàng)建一個(gè)XML編輯器chris 發(fā)表于2003-07-17 評(píng)價(jià):0/0 評(píng)論數(shù):0 點(diǎn)擊數(shù):2409 [收藏]出自:yesky
我想您一定對(duì)XML有所了解,說(shuō)不定您現(xiàn)在還躍躍欲試想寫(xiě)一段XML文本呢,可是現(xiàn)在能找到的跨平臺(tái)的、免費(fèi)的XML編輯器太少了。所以在本文中,我想介紹一下或者說(shuō)帶您一步一步的開(kāi)發(fā)一個(gè)簡(jiǎn)單的XML編輯器,當(dāng)然我們要用到一些最常見(jiàn)的Java 2 Swing組件,不過(guò)這些都是免費(fèi)的,有些是JDK中的,有些是可以從網(wǎng)上下載的。我想通過(guò)本文,你就可以創(chuàng)建一個(gè)屬于你自己的XML編輯器。 先讓我介紹一下本文輯寫(xiě)的思路。首先我想簡(jiǎn)要的討論一下XML和為什么樹(shù)型結(jié)構(gòu)比較適合用來(lái)顯示XML,然后我們來(lái)看一看JAXP API如何建立所需要的XML類的環(huán)境;然后我們將了解用來(lái)顯示一個(gè)圖形樹(shù)的JTree Swing組件;最后,我們將創(chuàng)建一個(gè)繼承JTree組件的可以重復(fù)使用的類,可以用來(lái)分析一個(gè)XML文檔,并把數(shù)據(jù)顯示在一個(gè)Jtree中。 說(shuō)到XML(eXtensible Markup Languge),人們往往把它當(dāng)成是一種新的用于Web瀏覽器中的標(biāo)記語(yǔ)言,就象HTML或CSS一樣。其實(shí),XML是一種數(shù)據(jù)表示語(yǔ)言,它允許你使用一種非常有效的方法來(lái)描述你的數(shù)據(jù)。XML能夠使你定義諸如“these three words constitutes a heading”這樣的語(yǔ)句。XML允許你聲明任何類型的數(shù)據(jù),而不是用來(lái)把這些數(shù)據(jù)顯示在網(wǎng)頁(yè)中。 請(qǐng)看一看下面的XML實(shí)例: <article> <header> <title> 使用Java Swing 創(chuàng)建一個(gè)XML編輯器 <subtitle> 第一部分</subtitle> </title> <author> Wayne </author> <header> <content> 這是正文</content> </article> 請(qǐng)注意,這些元素和標(biāo)準(zhǔn)的HTML語(yǔ)句是不同的,但是它們看上去比較象HTML,這是因?yàn)閄ML和HTML都是來(lái)源于SGML語(yǔ)言。不同的是HTML有預(yù)定義的標(biāo)簽集,而XML的語(yǔ)法則有許多靈活性,它允許你使用表意的標(biāo)記如<author>來(lái)括在數(shù)據(jù)兩邊。你還要注意,所有的元素都從屬于根元素(上例中為<article>),有些元素則還有自己的子元素,如<subtitle>就是<title>的子元素。這樣的數(shù)據(jù)組織方式有三個(gè)好處:數(shù)據(jù)能夠更加表意,數(shù)據(jù)更加易維護(hù)而且數(shù)據(jù)更加容易作為一個(gè)樹(shù)的結(jié)構(gòu)表現(xiàn)出來(lái),這就是我們?yōu)槭裁词褂肑Tree對(duì)象來(lái)顯示XML數(shù)據(jù)的原因。如果你想對(duì)XML有更深的了解,請(qǐng)參閱天極網(wǎng)上的相關(guān)教程。 JAXP是一個(gè)用于處理XML的Java API,它能夠使應(yīng)用程序分析并且轉(zhuǎn)化XML文檔,它的功能有點(diǎn)象JDBC API,都是把函數(shù)功能抽象成一個(gè)個(gè)方法。你可以去Apache網(wǎng)站找到最新的Xerces分析器,其中含有最新的JAXP,下載下來(lái)以后把它放在你的類目錄中。 下面讓我們看一下如何使用JTree Swing組件。 我們都知道,在自然界中,一棵樹(shù)通常都有一個(gè)非常粗的樹(shù)干,樹(shù)干上有許多樹(shù)枝分叉。每個(gè)樹(shù)杈和樹(shù)杈之間都有一定的聯(lián)系,因?yàn)樗鼈兌加型粋€(gè)來(lái)源:樹(shù)干。這種繼承的關(guān)系并不只在樹(shù)枝中有,人類譜系也遵循相同的規(guī)律。從父母,到子女再到子女的子女,就這樣直到數(shù)不清為止。同樣,在數(shù)據(jù)存儲(chǔ)中,樹(shù)的概念也是一種使用同人類家譜樹(shù)一樣方法儲(chǔ)存數(shù)據(jù)的方法。樹(shù)的每一個(gè)樹(shù)杈稱為一個(gè)節(jié)點(diǎn),每個(gè)有子節(jié)點(diǎn)的節(jié)點(diǎn)稱為父節(jié)點(diǎn),所有的子節(jié)點(diǎn)的公共的父節(jié)點(diǎn)被稱為根節(jié)點(diǎn)。一個(gè)JTree組件就是一個(gè)簡(jiǎn)單的樹(shù)數(shù)據(jù)結(jié)構(gòu)的可視化表現(xiàn)形式。 幾乎所有的XML編輯器都包括一個(gè)可視化的樹(shù)結(jié)構(gòu),能讓你編輯XML文檔中的元素。我們馬上就會(huì)構(gòu)建一個(gè)編輯器,不過(guò)在此之前,先讓我們?cè)倭私庖幌翵Tree組件。一個(gè)節(jié)點(diǎn)在一棵樹(shù)的某個(gè)位置儲(chǔ)存數(shù)據(jù),為了存儲(chǔ)數(shù)據(jù),必須知道任何一個(gè)父節(jié)點(diǎn)和它們的子節(jié)點(diǎn)。javax.swing.tree包定義了一些非常有用的接口,提供了一種通用的方法構(gòu)建和操作一個(gè)樹(shù)結(jié)構(gòu)。 TreeNode方法,用于訪問(wèn)樹(shù)的節(jié)點(diǎn)的信息 MutableTreeNode方法 用在一個(gè)可變的樹(shù)上(能夠添加或刪除子節(jié)點(diǎn)) TreeModel方法 用于創(chuàng)建和管理與樹(shù)有關(guān)的數(shù)據(jù)模型。 接下來(lái),我們將創(chuàng)建一個(gè)繼承JTree的類,提供分析XML文檔和用可視化JTree組件把節(jié)點(diǎn)顯示出來(lái)的功能。 創(chuàng)建XTree組件 XTree類由一個(gè)構(gòu)造函數(shù)和三個(gè)方法組成,為了簡(jiǎn)單起見(jiàn)我們的組件只能構(gòu)建一個(gè)Xtree,在樹(shù)創(chuàng)建好之后不能進(jìn)行處理它的節(jié)點(diǎn)。下面讓我們來(lái)看一個(gè)這個(gè)類。 域: private DefaultMutableTreeNode treeNode 這個(gè)成員變量?jī)?chǔ)存TreeNode對(duì)象用于存儲(chǔ)JTree的模型?!?BR> DefaultMutableTreeNode類是在javax.swing.tree中被定義的,默認(rèn)提供了MutableTreeNode接口的一個(gè)實(shí)現(xiàn)。 private DocumentBuilderFactory dbf private DocumentBuilder db private Document doc 這三個(gè)成員變量是JAXP的一部分,用來(lái)分析XML文本并轉(zhuǎn)化成DOM(Document Object Model) 對(duì)象。 構(gòu)造函數(shù) public XTree( String text ) 這個(gè)構(gòu)造函數(shù)通過(guò)使用傳送到構(gòu)造器中的XML文本創(chuàng)建一個(gè)XTree對(duì)象。在初始化一些與JTree超類和DOM分析對(duì)象有關(guān)的基本顯示屬性后,構(gòu)造函數(shù)生成一個(gè)TreeModel 對(duì)象用來(lái)創(chuàng)建一個(gè)實(shí)際可視的樹(shù)。通過(guò)把DOM對(duì)象傳送到createTreeNode()方法來(lái)創(chuàng)建一個(gè)根節(jié)點(diǎn),createTreeNode()方法返回一個(gè)DefaultMutableTreeNode類型的對(duì)象。這個(gè)對(duì)象然后被用來(lái)創(chuàng)建樹(shù)的TreeModel。 方法 private DefaultMutableTreeNode createTreeNode( Node root ) 這個(gè)方法采用一個(gè)DOM 節(jié)點(diǎn),然后在子節(jié)點(diǎn)中遞歸直到所有的接點(diǎn)都被添加到DefaultMutableTreeNode中。這是一個(gè)遞歸方法,為了找到根節(jié)點(diǎn)下的每一個(gè)子節(jié)點(diǎn),它每次都要調(diào)用自己。JTree然后就可以使用DefaultMutableTreeNode對(duì)象了,因?yàn)樗呀?jīng)是樹(shù)型了。 private String getNodeType( Node node ) 這個(gè)方法,被createTreeNode()用來(lái)聯(lián)系一個(gè)字符串和某一種類型的節(jié)點(diǎn)。 private Node parseXml() 這個(gè)方法,用來(lái)分析XML文本字符串,它返回Node類型的對(duì)象,能夠被傳送到createTreeNode()方法中。 下面我給出了java代碼,供大家分析研究。 // 到入W3C的DOM 類 import org.w3c.dom.*; // JAXP的用于DOM I/O的類 import javax.xml.parsers.*; // 標(biāo)準(zhǔn)Java類 import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import java.io.*; public class XTree extends JTree { /** * 這個(gè)成員變量?jī)?chǔ)存TreeNode對(duì)象用于存儲(chǔ)JTree的模型。 *DefaultMutableTreeNode類是在javax.swing.tree中被定義的 *默認(rèn)提供了MutableTreeNode接口的一個(gè)實(shí)現(xiàn)。 */ private DefaultMutableTreeNode treeNode; /** * 這三個(gè)成員變量是JAXP的一部分,用來(lái)分析XML文本并轉(zhuǎn)化成DOM(Document Object Model) 對(duì)象。 */ private DocumentBuilderFactory dbf; private DocumentBuilder db; private Document doc; /** * 這個(gè)構(gòu)造函數(shù)通過(guò)使用傳送到構(gòu)造器中的XML文本創(chuàng)建一個(gè)XTree對(duì)象 * @參數(shù) text是一個(gè)XML格式的XML文本 * @異常 ParserConfigurationException 如果構(gòu)造函數(shù)非正常的設(shè)置分析器,就會(huì)拋出異常 */ public XTree( String text ) throws ParserConfigurationException { super(); // 設(shè)置Tree渲染的基本屬性 getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION ); setShowsRootHandles( true ); setEditable( false ); // 允許樹(shù)可以編輯 // 通過(guò)初始化對(duì)象的DOM來(lái)分析對(duì)象 dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating( false ); db = dbf.newDocumentBuilder(); // 采用DOM根節(jié)點(diǎn)并且把它轉(zhuǎn)化成JTree的樹(shù)模型 treeNode = createTreeNode( parseXml( text ) ); setModel( new DefaultTreeModel( treeNode ) ); } file://中止XTree() /** * 這個(gè)方法采用一個(gè)DOM 節(jié)點(diǎn),然后在子節(jié)點(diǎn)中遞歸直到所有的接點(diǎn)都被添加到DefaultMutableTreeNode中。 * 這是一個(gè)遞歸方法,為了找到根節(jié)點(diǎn)下的每一個(gè)子節(jié)點(diǎn),它每次都要調(diào)用自己。 * JTree然后就可以使用DefaultMutableTreeNode對(duì)象了,因?yàn)樗呀?jīng)是樹(shù)型了。 * * @參數(shù) root org.w3c.Node.Node * * @返回值 返回一個(gè)基于根節(jié)點(diǎn)DefaultMutableTreeNode對(duì)象 */ private DefaultMutableTreeNode createTreeNode( Node root ) { DefaultMutableTreeNode treeNode = null; String type, name, value; NamedNodeMap attribs; Node attribNode; // 從根節(jié)點(diǎn)中取得數(shù)據(jù) type = getNodeType( root ); name = root.getNodeName(); value = root.getNodeValue(); treeNode = new DefaultMutableTreeNode( root.getNodeType() == Node.TEXT_NODE ? value : name ); // 顯示屬性 attribs = root.getAttributes(); if( attribs != null ) { for( int i = 0; i < attribs.getLength(); i++ ) { attribNode = attribs.item(i); name = attribNode.getNodeName().trim(); value = attribNode.getNodeValue().trim(); if ( value != null ) { if ( value.length() > 0 ) { treeNode.add( new DefaultMutableTreeNode( "[Attribute] --> " + name + "=\"" + value + "\"" ) ); } file://end if ( value.length() > 0 ) } file://end if ( value != null ) } file://end for( int i = 0; i < attribs.getLength(); i++ ) } file://end if( attribs != null ) // 如果存在子節(jié)點(diǎn),遞歸 if( root.hasChildNodes() ) { NodeList children; int numChildren; Node node; String data; children = root.getChildNodes(); // 如果子節(jié)點(diǎn)非空的話,只遞歸 if( children != null ) { numChildren = children.getLength(); for (int i=0; i < numChildren; i++) { node = children.item(i); if( node != null ) { if( node.getNodeType() == Node.ELEMENT_NODE ) { treeNode.add( createTreeNode(node) ); } file://end if( node.getNodeType() == Node.ELEMENT_NODE ) data = node.getNodeValue(); if( data != null ) { data = data.trim(); if ( !data.equals("\n") && !data.equals("\r\n") && data.length() > 0 ) { treeNode.add(createTreeNode(node)); } file://end if ( !data.equals("\n") && !data.equals("\r\n") && data.length() > 0 ) } file://end if( data != null ) } file://end if( node != null ) } file://end for (int i=0; i < numChildren; i++) } file://end if( children != null ) } file://end if( root.hasChildNodes() ) return treeNode; } file://end createTreeNode( Node root ) /** * 這個(gè)方法,被createTreeNode()用來(lái)聯(lián)系一個(gè)字符串和某一種類型的節(jié)點(diǎn)。 * * @參數(shù) node org.w3c.Node.Node * * @返回值 返回顯示節(jié)點(diǎn)類的字符串 */ private String getNodeType( Node node ) { String type; switch( node.getNodeType() ) { case Node.ELEMENT_NODE: { type = "Element"; break; } case Node.ATTRIBUTE_NODE: { type = "Attribute"; break; } case Node.TEXT_NODE: { type = "Text"; break; } case Node.CDATA_SECTION_NODE: { type = "CData section"; break; } case Node.ENTITY_REFERENCE_NODE: { type = "Entity reference"; break; } case Node.ENTITY_NODE: { type = "Entity"; break; } case Node.PROCESSING_INSTRUCTION_NODE: { type = "Processing instruction"; break; } case Node.COMMENT_NODE: { type = "Comment"; break; } case Node.DOCUMENT_NODE: { type = "Document"; break; } case Node.DOCUMENT_TYPE_NODE: { type = "Document type"; break; } case Node.DOCUMENT_FRAGMENT_NODE: { type = "Document fragment"; break; } case Node.NOTATION_NODE: { type = "Notation"; break; } default: { type = "???"; break; } }// 結(jié)束 switch( node.getNodeType() ) return type; } file://結(jié)束 getNodeType() /** * 這個(gè)方法,用來(lái)分析XML文本字符串,它返回Node類型的對(duì)象,能夠被傳送到createTreeNode()方法中。 * * @參數(shù) text 一個(gè)顯示XML文檔的字符串 * @返回值 返回一個(gè)org.w3c.Node.Node對(duì)象 */ private Node parseXml( String text ) { ByteArrayInputStream byteStream; byteStream = new ByteArrayInputStream( text.getBytes() ); try { doc = db.parse( byteStream ); } catch ( Exception e ) { e.printStackTrace(); System.exit(0); } return ( Node )doc.getDocumentElement(); } file://結(jié)束 parseXml() } file://結(jié)束 class XTree 代碼2 XTreeTester.java import javax.xml.parsers.*; // GUI類 import javax.swing.*; import java.awt.*; import java.awt.event.*; file://標(biāo)準(zhǔn) Java類 import java.io.*; public class XTreeTester extends JFrame { // XTree對(duì)象,用來(lái)在JTree中顯示XML private XTree xTree; // JScrollPane是JTree的容器 private JScrollPane jScroll; private WindowListener winClosing; // 設(shè)置框架的寬和高 private static final int FRAME_WIDTH = 400; private static final int FRAME_HEIGHT = 300; /* * 構(gòu)造器構(gòu)造一個(gè)框架包含JScrollPane, * 把一個(gè)基于XML字符串的XTree對(duì)象傳到構(gòu)造函數(shù)中 */ public XTreeTester( String title, String xml ) throws ParserConfigurationException { super( title ); Toolkit toolkit; Dimension dim, minimumSize; int screenHeight, screenWidth; // 初始化基本的布局屬性 setBackground( Color.lightGray ); getContentPane().setLayout( new BorderLayout() ); toolkit = Toolkit.getDefaultToolkit(); dim = toolkit.getScreenSize(); screenHeight = dim.height; screenWidth = dim.width; setBounds( (screenWidth-FRAME_WIDTH)/2, (screenHeight-FRAME_HEIGHT)/2, FRAME_WIDTH, FRAME_HEIGHT ); // 構(gòu)建XTree對(duì)象 xTree = new XTree( xml ); file://把XTree封裝到JScroll中,以便在JFrame可以使它在屏幕中上下滾動(dòng). jScroll = new JScrollPane(); jScroll.getViewport().add( xTree ); // 添加滾動(dòng)條到框架中 getContentPane().add( jScroll, BorderLayout.CENTER ); validate(); setVisible(true); // 添加WindowListener用來(lái)關(guān)閉窗口 winClosing = new WindowAdapter() { public void windowClosing(WindowEvent e) { exit(); } }; addWindowListener(winClosing); } // 程序從這里開(kāi)始執(zhí)行。必須把一個(gè)以xml為擴(kuò)展名的XML文件傳送到這個(gè)方法中,其格式為java XTreeTester yourxmlfilename.xml public static void main( String[] args ) { String fileName = ""; BufferedReader reader; String line; StringBuffer xmlText; XTreeTester xTreeTester; // 創(chuàng)建一個(gè)基于特定XML文件的文檔對(duì)象 try { if( args.length > 0 ) { fileName = args[0]; if ( fileName.substring( fileName.indexOf( ‘.‘ ) ).equals( ".xml" ) ) { reader = new BufferedReader( new FileReader( fileName ) ); xmlText = new StringBuffer(); while ( ( line = reader.readLine() ) != null ) { xmlText.append( line ); } // 分析完文檔對(duì)象后將重寫(xiě)文件 reader.close(); // 構(gòu)造 GUI 組件 xTreeTester = new XTreeTester( "XTree 測(cè)試", xmlText.toString() ); } else { help(); } } else { help(); } } catch( FileNotFoundException fnfEx ) { System.out.println( "沒(méi)有發(fā)現(xiàn)"+ fileName + "文件。" ); exit(); } catch( Exception ex ) { ex.printStackTrace(); exit(); } } file://幫助信息 private static void help() { System.out.println( "\n使用方法:java XTreeTester yourxmlfilename.xml" ); System.exit(0); } // 退出 private static void exit() { System.out.println( "\n謝謝使用 XTree" ); System.exit(0); } } |
|