我想您一定对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); } } |
闽公网安备 35060202000074号