出自:yesky
我想您一定对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("\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 ) /** * 这个方法,被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( "\n使用方法:java XTreeTester yourxmlfilename.xml" ); System.exit(0); } // 退出 private static void exit() { System.out.println( "\n谢谢使用 XTree" ); System.exit(0); } } |
|