使用JavaSwing創建一個XML編輯器
我想您一定對XML有所了解,說不定您現在還躍躍欲試想寫一段XML文本呢,可是現在能找到的跨平臺的、免費的XML編輯器太少了。所以在本文中,我想介紹一下或者說帶您一步一步的開發一個簡單的XML編輯器,當然我們要用到一些最常見的Java 2 Swing組件,不過這些都是免費的,有些是JDK中的,有些是可以從網上下載的。我想通過本文,你就可以創建一個屬于你自己的XML編輯器。
先讓我介紹一下本文輯寫的思路。首先我想簡要的討論一下XML和為什么樹型結構比較適合用來顯示XML,然后我們來看一看JAXP API如何建立所需要的XML類的環境;然后我們將了解用來顯示一個圖形樹的JTree Swing組件;最后,我們將創建一個繼承JTree組件的可以重復使用的類,可以用來分析一個XML文檔,并把數據顯示在一個Jtree中。
說到XML(eXtensible Markup Languge),人們往往把它當成是一種新的用于Web瀏覽器中的標記語言,就象HTML或CSS一樣。其實,XML是一種數據表示語言,它允許你使用一種非常有效的方法來描述你的數據。XML能夠使你定義諸如“these three words constitutes a heading”這樣的語句。XML允許你聲明任何類型的數據,而不是用來把這些數據顯示在網頁中。
請看一看下面的XML實例:
<article> <header> <title> 使用Java Swing 創建一個XML編輯器 <subtitle> 第一部分</subtitle> </title> <author> Wayne </author> <header> <content> 這是正文</content> </article> |
請注意,這些元素和標準的HTML語句是不同的,但是它們看上去比較象HTML,這是因為XML和HTML都是來源于SGML語言。不同的是HTML有預定義的標簽集,而XML的語法則有許多靈活性,它允許你使用表意的標記如<author>來括在數據兩邊。你還要注意,所有的元素都從屬于根元素(上例中為<article>),有些元素則還有自己的子元素,如<subtitle>就是<title>的子元素。這樣的數據組織方式有三個好處:數據能夠更加表意,數據更加易維護而且數據更加容易作為一個樹的結構表現出來,這就是我們為什么使用JTree對象來顯示XML數據的原因。如果你想對XML有更深的了解,請參閱天極網上的相關教程。
JAXP是一個用于處理XML的Java API,它能夠使應用程序分析并且轉化XML文檔,它的功能有點象JDBC API,都是把函數功能抽象成一個個方法。你可以去Apache網站找到最新的Xerces分析器,其中含有最新的JAXP,下載下來以后把它放在你的類目錄中。
下面讓我們看一下如何使用JTree Swing組件。
我們都知道,在自然界中,一棵樹通常都有一個非常粗的樹干,樹干上有許多樹枝分叉。每個樹杈和樹杈之間都有一定的聯系,因為它們都有同一個來源:樹干。這種繼承的關系并不只在樹枝中有,人類譜系也遵循相同的規律。從父母,到子女再到子女的子女,就這樣直到數不清為止。同樣,在數據存儲中,樹的概念也是一種使用同人類家譜樹一樣方法儲存數據的方法。樹的每一個樹杈稱為一個節點,每個有子節點的節點稱為父節點,所有的子節點的公共的父節點被稱為根節點。一個JTree組件就是一個簡單的樹數據結構的可視化表現形式。
幾乎所有的XML編輯器都包括一個可視化的樹結構,能讓你編輯XML文檔中的元素。我們馬上就會構建一個編輯器,不過在此之前,先讓我們再了解一下JTree組件。一個節點在一棵樹的某個位置儲存數據,為了存儲數據,必須知道任何一個父節點和它們的子節點。javax.swing.tree包定義了一些非常有用的接口,提供了一種通用的方法構建和操作一個樹結構。
TreeNode方法,用于訪問樹的節點的信息
MutableTreeNode方法 用在一個可變的樹上(能夠添加或刪除子節點)
TreeModel方法 用于創建和管理與樹有關的數據模型。
接下來,我們將創建一個繼承JTree的類,提供分析XML文檔和用可視化JTree組件把節點顯示出來的功能。
創建XTree組件
XTree類由一個構造函數和三個方法組成,為了簡單起見我們的組件只能構建一個Xtree,在樹創建好之后不能進行處理它的節點。下面讓我們來看一個這個類。
域:
private DefaultMutableTreeNode treeNode 這個成員變量儲存TreeNode對象用于存儲JTree的模型。
DefaultMutableTreeNode類是在javax.swing.tree中被定義的,默認提供了MutableTreeNode接口的一個實現。
private DocumentBuilderFactory dbf
private DocumentBuilder db
private Document doc 這三個成員變量是JAXP的一部分,用來分析XML文本并轉化成DOM(Document Object Model) 對象。
構造函數
public XTree( String text )
這個構造函數通過使用傳送到構造器中的XML文本創建一個XTree對象。在初始化一些與JTree超類和DOM分析對象有關的基本顯示屬性后,構造函數生成一個TreeModel 對象用來創建一個實際可視的樹。通過把DOM對象傳送到createTreeNode()方法來創建一個根節點,createTreeNode()方法返回一個DefaultMutableTreeNode類型的對象。這個對象然后被用來創建樹的TreeModel。
方法
private DefaultMutableTreeNode createTreeNode( Node root )
這個方法采用一個DOM 節點,然后在子節點中遞歸直到所有的接點都被添加到DefaultMutableTreeNode中。這是一個遞歸方法,為了找到根節點下的每一個子節點,它每次都要調用自己。JTree然后就可以使用DefaultMutableTreeNode對象了,因為它已經是樹型了。
private String getNodeType( Node node )
這個方法,被createTreeNode()用來聯系一個字符串和某一種類型的節點。
private Node parseXml()
這個方法,用來分析XML文本字符串,它返回Node類型的對象,能夠被傳送到createTreeNode()方法中。
下面我給出了java代碼,供大家分析研究。
// 到入W3C的DOM 類 import org.w3c.dom.*; // JAXP的用于DOM I/O的類 import javax.xml.parsers.*; // 標準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 { /** * 這個成員變量儲存TreeNode對象用于存儲JTree的模型。 *DefaultMutableTreeNode類是在javax.swing.tree中被定義的 *默認提供了MutableTreeNode接口的一個實現。 */ private DefaultMutableTreeNode treeNode; /** * 這三個成員變量是JAXP的一部分,用來分析XML文本并轉化成DOM(Document Object Model) 對象。 */ private DocumentBuilderFactory dbf; private DocumentBuilder db; private Document doc; |
/**
* 這個構造函數通過使用傳送到構造器中的XML文本創建一個XTree對象
* @參數 text是一個XML格式的XML文本
* @異常 ParserConfigurationException 如果構造函數非正常的設置分析器,就會拋出異常
*/
public XTree( String text ) throws ParserConfigurationException { super(); // 設置Tree渲染的基本屬性 getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION ); setShowsRootHandles( true ); setEditable( false ); // 允許樹可以編輯 // 通過初始化對象的DOM來分析對象 dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating( false ); db = dbf.newDocumentBuilder(); // 采用DOM根節點并且把它轉化成JTree的樹模型 treeNode = createTreeNode( parseXml( text ) ); setModel( new DefaultTreeModel( treeNode ) ); } file://中止XTree() |
/**
* 這個方法采用一個DOM 節點,然后在子節點中遞歸直到所有的接點都被添加到DefaultMutableTreeNode中。
* 這是一個遞歸方法,為了找到根節點下的每一個子節點,它每次都要調用自己。
* JTree然后就可以使用DefaultMutableTreeNode對象了,因為它已經是樹型了。
*
* @參數 root org.w3c.Node.Node
*
* @返回值 返回一個基于根節點DefaultMutableTreeNode對象
*/
private DefaultMutableTreeNode createTreeNode( Node root ) { DefaultMutableTreeNode treeNode = null; String type, name, value; NamedNodeMap attribs; Node attribNode; // 從根節點中取得數據 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 ) // 如果存在子節點,遞歸 if( root.hasChildNodes() ) { NodeList children; int numChildren; Node node; String data; children = root.getChildNodes(); // 如果子節點非空的話,只遞歸 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("") && !data.equals("") && data.length() > 0 ) { treeNode.add(createTreeNode(node)); } file://end if ( !data.equals("") && !data.equals("") && 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 ) |
/**
* 這個方法,被createTreeNode()用來聯系一個字符串和某一種類型的節點。
*
* @參數 node org.w3c.Node.Node
*
* @返回值 返回顯示節點類的字符串
*/
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; } }// 結束 switch( node.getNodeType() ) return type; } file://結束 getNodeType() |
/**
* 這個方法,用來分析XML文本字符串,它返回Node類型的對象,能夠被傳送到createTreeNode()方法中。
*
* @參數 text 一個顯示XML文檔的字符串
* @返回值 返回一個org.w3c.Node.Node對象
*/
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://結束 parseXml() } file://結束 class XTree |
代碼2 XTreeTester.java
import javax.xml.parsers.*; // GUI類 import javax.swing.*; import java.awt.*; import java.awt.event.*; file://標準 Java類 import java.io.*; public class XTreeTester extends JFrame { // XTree對象,用來在JTree中顯示XML private XTree xTree; // JScrollPane是JTree的容器 private JScrollPane jScroll; private WindowListener winClosing; // 設置框架的寬和高 private static final int FRAME_WIDTH = 400; private static final int FRAME_HEIGHT = 300; |
/*
* 構造器構造一個框架包含JScrollPane,
* 把一個基于XML字符串的XTree對象傳到構造函數中
*/
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 ); // 構建XTree對象 xTree = new XTree( xml ); file://把XTree封裝到JScroll中,以便在JFrame可以使它在屏幕中上下滾動. jScroll = new JScrollPane(); jScroll.getViewport().add( xTree ); // 添加滾動條到框架中 getContentPane().add( jScroll, BorderLayout.CENTER ); validate(); setVisible(true); // 添加WindowListener用來關閉窗口 winClosing = new WindowAdapter() { public void windowClosing(WindowEvent e) { exit(); } }; addWindowListener(winClosing); } |
// 程序從這里開始執行。必須把一個以xml為擴展名的XML文件傳送到這個方法中,其格式為java XTreeTester yourxmlfilename.xml
public static void main( String[] args ) { String fileName = ""; BufferedReader reader; String line; StringBuffer xmlText; XTreeTester xTreeTester; // 創建一個基于特定XML文件的文檔對象 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 ); } // 分析完文檔對象后將重寫文件 reader.close(); // 構造 GUI 組件 xTreeTester = new XTreeTester( "XTree 測試", xmlText.toString() ); } else { help(); } } else { help(); } } catch( FileNotFoundException fnfEx ) { System.out.println( "沒有發現"+ fileName + "文件。" ); exit(); } catch( Exception ex ) { ex.printStackTrace(); exit(); } } |
file://幫助信息
private static void help() { System.out.println( "使用方法:java XTreeTester yourxmlfilename.xml" ); System.exit(0); } // 退出 private static void exit() { System.out.println( "謝謝使用 XTree" ); System.exit(0); } } |