使用JavaSwing創建一個XML編輯器之二
這是本系列的第二篇文章。在上一篇文章中,我們簡要地討論了XML以及為什么一個樹形結構適合顯示XML、如何處理XML數據、如何使用JTree Swing 組件以及如何創建一個可重用的能夠分析XML文檔以及在Jtree顯示數據的組件。
在本文中,我們將創建我們的XML編輯器的框架,為了達到這個目的,我們將用到許多Swing組件(包括JsplitPane、JscrollPane、Jbutton和JtextArea組件)。
一、問題的提出與解決
我如何創建一個能夠瀏覽文本和瀏覽視圖的XML文本編輯器呢?創建一個包含Jbutton和JsplitPane的Jframe對象, 然后讓JsplitPane對象包含兩個JscrollPane對象,一個用于瀏覽圖形(xTree類),另一個用于瀏覽文本(JtextArea類)。Jbutton用來管理刷新圖形瀏覽的操作。
二、增強Xtree類的功能
在上一篇文章中,我們開發了Xtree類,這是一個可重用的組件,繼承于Jtree類并能夠把XML數據以圖形樹的形式顯示。我們現在就增強這個類, 通過提供給它一個在顯示默認的XML樹來We will now enhance that class by providing it with a default XML tree to display in the event that an XML file is not supplied at the command-line. 而且,我們還將添加一些錯誤處理邏輯以便程序不會因為無效的XML而崩潰。
第一步是創建一個名為buildTree()的方法:
private DefaultTreeModel buildTree( String text ) { DefaultMutableTreeNode treeNode; Node newNode; // 采用DOM根節點并把它轉化成為一個Tree模型 newNode = parseXml( text ); if ( newNode != null ) { treeNode = createTreeNode( newNode ); return new DefaultTreeModel( treeNode ); } else return null; } file://結束buildTree() |
這個方法取得傳入的 XML字符串,分析這個 XML字符串并構造一個可以用來從數據中構造圖形樹形結構的DefaultTreeModel變量實例。這個功能原來包含在 XTree()構造程序中,但是我們把它拿出來然后把它放進一個單獨的方法中,這樣我們就有了創建一個默認圖形樹的伸縮性。這就是我們接下來想做的事。
接下來一步是創建一個叫 buildWelcomeTree()的方法。這個方法一次構建一個DefaultTreeModel變量,而不是通過分析一個現有的XML文字字符串。如果用戶沒有指定 XML文件就啟動這個應用程序,將顯示 DefaultTreeModel。見代碼段1
代碼段1:
private DefaultTreeModel buildWelcomeTree() { DefaultMutableTreeNode root; DefaultMutableTreeNode instructions, openingDoc, editingDoc, savingDoc; DefaultMutableTreeNode openingDocText, editingDocText, savingDocText; DefaultMutableTreeNode development, addingFeatures, contactingKyle; root = new DefaultMutableTreeNode( "Welcome to XML View 1.0" ); instructions = new DefaultMutableTreeNode( "Instructions" ); openingDoc = new DefaultMutableTreeNode ( "Opening XML Documents" ); openingDocText = new DefaultMutableTreeNode ( "When invoking the XmlEditor from the command-line, you must specify the filename." ); editingDoc = new DefaultMutableTreeNode ( "Editing an XML Document" ); editingDocText = new DefaultMutableTreeNode ( "XML text in the right hand frame can be edited directly. The "refresh" button will rebuild the JTree in the left frame." ); savingDoc = new DefaultMutableTreeNode ( "Saving an XML Document" ); savingDocText = new DefaultMutableTreeNode ( "This iteration of the XmlEditor does not provide the ability to save your document. That will come with the next article." ); root.add( instructions ); instructions.add( openingDoc ); instructions.add( editingDoc ); openingDoc.add( openingDocText ); editingDoc.add( editingDocText ); return new DefaultTreeModel( root ); } |
接下來的我們需要添加一個新的構造程序來簡化默認顯示功能,我們將修改主構造程序,這樣它就不能接受任何參數,創建一個新的能接收單一的 XML文本字符串的構造程序。這樣以來,如果沒有 XML文本被顯示的話就會創建默認 XTree對象,而如果 XML文本被顯示的話將創建一個唯一的 XTree對象。代碼段2中給出了兩個構造程序。
代碼段2:
public XTree( String text ) throws ParserConfigurationException { this(); refresh( text ); } public XTree() throws ParserConfigurationException { super(); getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION ); setShowsRootHandles( true ); setEditable( false ); dbf = DocumentBuilderFactory.newInstance(); dbf.setValidating( false ); db = dbf.newDocumentBuilder(); setModel( buildWelcomeTree() ); } |
三、創建 XmlEditor類
XmlEditor類與前面的那篇文章的XTreeTester類有相同的用途,不同之處在于 XmlEditor包括一個 JTextArea,允許你操作文本版XML。之后,你可以點擊 “Refresh”按鈕并查看 XTree組件中反映出來的變化。
如果直接修改第一篇文章中的代碼,你就能節省很多時間,你可以把 XTreeTester.java文件重命名為 XmlEditor.java (不過一定要修改構造程序 )并把它當成模板文件。
要做的第一件事是添加下列 Swing組件:另一個 JScrollPane、 JSplitPane、 JTextArea和 JButton。通過聲明所有的這些組件以及其他組件開始(看代碼段3)。
代碼段3:
private XTree xTree; private JScrollPane jScroll, jScrollRt; private JSplitPane splitPane; private JButton refreshButton; private WindowListener winClosing; |
首先,我們將創建并添加“ Refresh”按鈕。這個按鈕用來指示這個 XTree組件將使用當前 XML文本來刷新。我們還需要使用一個ActionListener來注冊它。(參見代碼段 4 ) 為了攔截按鈕事件,我們需要有這個類還要實現 ActionListener,而且我們還需要創建一個 actionPerformed()方法 (參見代碼段 5 )。
代碼段4:
refreshButton = new JButton( "Refresh" ); refreshButton.setBorder( BorderFactory.createRaisedBevelBorder() ); refreshButton.addActionListener( this ); getContentPane().add( refreshButton, BorderLayout.NORTH ); |
代碼段5:
public void actionPerformed( ActionEvent ae ) { if ( ae.getActionCommand().equals( "Refresh" ) ) xTree.refresh( textArea.getText() ); } |
接下來,我們將創建新的 JScrollPane和 JTextArea并且把 JTextArea添加到 JScrollPane中。這樣,我們將得到包含 XTree組件的原始的 JScrollPane和包含 JTextArea組件的新的 JScrollPane。這個原始的 XTree構造程序還有一個修改之處。我們將刪除先前傳進這個方法的字符串參數。(該功能通過我們接下來要構建的另一個 XmlEditor()構造程序操作)我們把這兩個方框放進一個 JSplitPane中,它是一個有分隔器的組件,可以在這個分隔器的另一邊包含一個組件。(參見代碼段6)。
代碼段6:
jScroll = new JScrollPane(); jScrollRt = new JScrollPane(); textArea = new JTextArea( 200,150 ); jScrollRt.getViewport().add( textArea ); xTree = new XTree(); xTree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION ); xTree.setShowsRootHandles( true ); xTree.setEditable( false ); jScroll.getViewport().add( xTree ); splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, jScroll, jScrollRt ); splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(200); minimumSize = new Dimension(200, 150); jScroll.setMinimumSize( minimumSize ); jScrollRt.setMinimumSize( minimumSize ); splitPane.setPreferredSize( new Dimension(400, 300) ); getContentPane().add( splitPane, BorderLayout.CENTER ); |
現在我們需要修改這個構造程序來處理這個非 XML文件的情景。刪除當前構造程序需要的第二個字符串參數。這個構造程序現在就是默認的構造程序。我們將創建一個新的構造程序來接收一個字符串參數。它將先調用默認構造程序,然后處理這個參數。(參見代碼段7 )而且,main()方法必須被修改以便在沒有XML文件的事件中,仍然能創建一個默認的XmlEditor對象。(參見代碼段8)
代碼段7:
public XmlEditor( String title, ArrayList xmlText ) throws ParserConfigurationException { this( title ); textArea.setText( ( String )xmlText.get( 0 ) + "" ); for ( int i = 1; i < xmlText.size(); i++ ) textArea.append( ( String )xmlText.get( i ) + "" ); xTree.refresh( textArea.getText() ); } |
代碼段8:
public static void main( String[] args ) { String fileName = ""; BufferedReader reader; String line; ArrayList xmlText = null; XmlEditor xmlEditor; try { if( args.length > 0 ) { fileName = args[0]; if ( fileName.substring( fileName.indexOf( '.' ) ).equals( ".xml" ) ) { reader = new BufferedReader( new FileReader( fileName ) ); xmlText = new ArrayList(); while ( ( line = reader.readLine() ) != null ) { xmlText.add( line ); } reader.close(); xmlEditor = new XmlEditor( "XmlEditor 1.0", xmlText ); } else { help(); } } else { xmlEditor = new XmlEditor( "XmlEditor 1.0" ); } } catch( FileNotFoundException fnfEx ) { System.out.println( fileName + " was not found." ); exit(); } catch( Exception ex ) { ex.printStackTrace(); exit(); } } |
為了很容易地處理 JTextArea數據,還需要最后一次必要的修改。一般我們不把這段文本當成一個長的字符串,而是把它當成一系列字符串,每行表示一個包含在數組列表中的字符串。這需要修改我們的構造程序接收的參數,并且修改在先前把這個數據作為一個字符串傳入的那個main()方法中的任何調用。這還需要導入 java.util包 (參見代碼段8 ) 。
測試這個應用程序
最后,我們必須添加一些異常處理到 XTree類中。如果你運行這個應用程序并輸入無效的XML數據到 JTextArea中,這個程序就會崩潰。我們當然不想這樣的事情發生。目前,我們把出錯信息打印到命令行中。在下一篇文章中,我們將在一個圖形對話框中顯示出錯信息。
如果無效的或格式不好的 XML數據被輸入 JTextArea然后按“Refresh”按鈕,然后解析器將拋出一個錯誤,在本例中,parseXML()方法中包含的當前異常處理將立即退出。我們想要這個程序報告這個錯誤并返回一個空值來指出這個解析是不成功的。(見代碼段9)你可能注意到 XTree類中的“buildTree()”和“refresh()”這兩個方法有一個條件語句以避免處理空值。這是因為如果它們試著去處理一個空值它們也會報告異常。這個事件鏈保持程序流進行并且保持 XTree模型穩定。
最后,我們就可以測試這個應用程序,測試過程我在這里就不多說了。