Overview

It seems obvious enough: You have an XML document or fragment. XML is hierarchical. A Swing JTree displays hierarchical data. How do you make a JTree display your XML fragment?

If you understand that Swing's architecture uses MVC, you probably know you need a "model" that your JTree instance can be instructed to use. However, the only real concrete model class in the standard Swing API is the DefaultTableModel class. This class provides
objects to the tree that implement the TreeNode interface. If you have started down this path, subclassing and customizing the standard behavior of the DefaultTableModel and working with your own DefaultTreeNode objects just to display XML will quickly give
you a headache.

What Is the True Cost of Failure in Mobile?

Fortunately, the better answer is to bypass all the API-provided convenience classes and instead write your own custom implementation of the javax.swing.tree.TreeModel interface. This article will
show you what you need to do.

About this Article

I will be staying completely within the standard API, so no third-party XML libraries will be needed. If you need to get more familliar with the XML classes in the standard API, you might want to read one of my earlier articles "Working
with XML and Java
" or consult your favorite search engine. I also tend to use Java 5 syntax (generics and enhanced-for). The reader is assumed to be somewhat familiar with Swing.

The TreeModel Interface

This interface defines the following methods. I have borrowed the descriptions directly from the Java API documentation.

  • void addTreeModelListener(TreeModelListener l): Adds a listener for the TreeModelEvent posted after the tree changes.
  • void removeTreeModelListener(TreeModelListener l): Removes a listener previously added with addTreeModelListener.
  • Object getRoot() Returns the root of the tree.
  • int getChildCount(Object parent): Returns the number of children of parent.
  • Object getChild(Object parent, int index): Returns the child of parent at index index in the parent's child array.
  • boolean isLeaf(Object node): Returns true if node is a leaf.
  • int getIndexOfChild(Object parent, Object child): Returns the index of child in parent.
  • void valueForPathChanged(TreePath path, Object newValue): Messaged when the user has altered the value for the item identified by path to newValue.

I have listed the methods roughly in order of usage. When a JTree is given a TreeModel implementation to use, it registers itself as a TreeModelListener. (A good model implementation will alert all of its listeners if the structure of the tree changes, or if
a node value changes. This lets the tree know it needs to redraw itself.) The root node is consulted first, and then the children for each node are obtained to build up the display. The icon in the tree that appears to each entry is determined from the result
of whether that entry is a leaf or not. (Generally, if a node returns '0' for getChildCount, it should return 'true' for isLeaf... but, this is not set in stone.) Finally, if the values in the model are mutable (they can be edited) and the tree is editable,
the tree will communicate with the edited value with the model by way of the valueForPathChanged method. (This example won't use an editable tree, so you won't implement this last method.)

An important point to note is that, being an interface, the TreeModel class doesn't concern itself with exactly where the data comes from or how it is stored. Because you will be dealing with an XML document, however, your implementation will include
an instance field for an org.w3c.dom.Document class, and a corresponding getter/setter method pair. The TreeModel interface methods will simply work off of the Document object directly:

protected Document document;

public Document getDocument() {
return document;
} public void setDocument(Document doc) {
this.document = doc;
TreeModelEvent evt = new TreeModelEvent(this,
new TreePath(getRoot()));
for (TreeModelListener listener : listeners) {
listener.treeStructureChanged(evt);
}
}

Any time you alter the data model—such as when you replace the source XML document completely in the setDocument method—you need to alert all the listeners of this fact. This is what the extra code in setDocument takes care of. (The definition of the "listeners"
variable will be introduced shortly. Bear with me.)

Before you start writing the TreeModel method implementations, you should carefully note that the model returns back objects of type java.lang.Object to the tree (for the root node and all children underneath it). The tree, by way of its default TreeCellRenderer,
neither knows nor cares exactly what class these objects truly are. To know what label to draw in the tree for any given node, the toString method is called. (A special-purpose, highly customized tree might use a different tree cell renderer, which might be
written to use something other than 'toString' to generate the node labels ... but that is beyond the scope of this article.) With this being the case, the standard org.w3c.dom.Node class doesn't provide a terribly useful return value for the toString method...
at least not that useful to the casual end-user. To address this, you will wrap the Element objects in a custom class that provides a more intelligent response to the toString method: It will return the name of the Element using the getNodeName method. Look
at this wrapper class first:

public class XMLTreeNode {
Element element;
public XMLTreeNode(Element element) {
this.element = element;
}
public Element getElement() {
return element;
}
public String toString() {
return element.getNodeName();
}
}

Another minor thing to nail down relates to the nature of "pretty-printed" XML and the return value of a Node object's getChildNodes() method. (You might want to read my previous article that discusses this. The link is in the Overview section.) My design requirement
is that I only want to show actual Element objects in the tree, not text nodes, comments, attributes, or other non-Element node types. To address this, a helper method is written to return a Vector of all the children of a given node that are specifically
of the Element type:

private Vector<Element> getChildElements(Node node) {
Vector<Element> elements = new Vector<Element>();
NodeList list = node.getChildNodes();
for (int i=0 ; i<list.getLength() ; i++) {
if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
elements.add( (Element) list.item(i));
}
}
return elements;
}

You will see this method used in several of the implementation methods, which you can finally address. The first item of business is to keep track of the registered TreeModelListeners:

Vector<TreeModelListener> listeners =
new Vector<TreeModelListener>(); public void addTreeModelListener(TreeModelListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
} public void removeTreeModelListener(TreeModelListener
listener) {
listeners.remove(listener);
}

For the next item, you need to provide the root node for the tree. This will be the root node of our document object, wrapped in the XMLTreeNode explained above:

public Object getRoot() {
if (document==null) {
return null;
}
Vector<Element> elements = getChildElements(document);
if (elements.size() > 0) {
return new XMLTreeNode( elements.get(0));
}
else {
return null;
}
}
What Is the True Cost of Failure in Mobile?

Returning null is completely fine, and signals to the JTree object that there is nothing to be displayed. A quick safety check allows the JTree to function during initialization before the model is handed a Document to work with. (Failing to do this will generate
NullPointerExceptions.)

Now that the tree has the root node, it will start asking for the number of child nodes under each node, and will ask for each of those child nodes in turn. The returned children objects will again be Element objects wrapped in an XMLTreeNode object.

public int getChildCount(Object parent) {
if (parent instanceof XMLTreeNode) {
Vector<Element> elements =
getChildElements( ((XMLTreeNode)parent).getElement() );
return elements.size();
}
return 0;
} public Object getChild(Object parent, int index) {
if (parent instanceof XMLTreeNode) {
Vector<Element> elements =
getChildElements( ((XMLTreeNode)parent).getElement() );
return new XMLTreeNode( elements.get(index) );
}
else {
return null;
}
} public int getIndexOfChild(Object parent, Object child) {
if (parent instanceof XMLTreeNode &&
child instanceof XMLTreeNode) {
Element pElement = ((XMLTreeNode)parent).getElement();
Element cElement = ((XMLTreeNode)child).getElement();
if (cElement.getParentNode() != pElement) {
return -1;
}
Vector<Element> elements = getChildElements(pElement);
return elements.indexOf(cElement);
}
return -1;
}

Now, to inform the default table cell renderer which icon to draw for a given node, the follow method is consulted:

public boolean isLeaf(Object node) {
if (node instanceof XMLTreeNode) {
Element element = ((XMLTreeNode)node).getElement();
Vector<Element> elements = getChildElements(element);
return elements.size()==0;
}
else {
return true;
}
}


The final method is not needed for the purposes of the demo because you are not allowing edits to be made to the tree. Therefore, a dummy implementation is given:

public void valueForPathChanged(TreePath path, Object
newValue) {
throw new UnsupportedOperationException();
}

That's everything you need. The rest just involves a little code to create a demo interface with a JTree in it, to load an XML-formatted file into a Document object, and to pass this off to an XMLTreeModel instance. I will leave most of these details for the
reader to experiment with, but will offer at least one parting screenshot. Consider the following XML document:

<?xml version="1.0"?>
<root>
<settings>This is a test.</settings>
<body>
<title>My Title</title>
<info>The quick brown fox jumps over the lazy dog.</info>
</body>
</root>

An interface that includes the JTree itself (and also includes code that listens for user selections of nodes in the tree to display text content into a standard JTextField) is available in the downloadable
code bundle
. The screenshot on a Mac system looks as follows:

What Is the True Cost of Failure in Mobile?

Conclusion

Writing a custom data model to display an XML document in a Swing JTree is ultimately rather easy. It's certainly easier than trying to get the "DefaultXXX" convenience classes to work off of the same data source. (How easy it is to sometimes forget this and
try to shoehorn a problem into a convenience implementation for which it wasn't designed!) Of course, care must be exercised to clearly identify just exactly what parts of the XML document need to be diplayed, and this should be clearly spelled out before
the TreeModel implementation is written. (For this demo, all Element-type nodes in the document were shown in the tree.)

Download the Code

You can download the code that accompanies this article here.

About the Author

Rob Lybarger is a software guy (and a degreed aerospace engineer) in the Houston, TX area who has been using Java for nearly ten years. He has used various versions of Windows, various distributions of Linux, but loves Mac OS X. He likes exploring
new techniques and new approaches to organizing and wiring applications, and loves to let the computer do as much work for him as possible.




Displaying XML in a Swing JTree的更多相关文章

  1. Swing组件Jtree,JTablePane选项卡运用

    今天开始写技术博客,说实话,本没有什么技术,说是总结也好,说是分享也罢,总之是想自己有意识的做一些事情,作为一名即将毕业的大学生,总是想以最好的状态,去面向社会,今天就是我准备好了的时候,本人将技术博 ...

  2. Java Swing 树状组件JTree的使用方法(转)

    树中特定的节点可以由 TreePath(封装节点及其所有祖先的对象)标识,或由其显示行(其中显示区域中的每一行都显示一个节点)标识.展开 节点是一个非叶节点(由返回 false 的 TreeModel ...

  3. 详解Swing中JTree组件的功能

    JTree组件是另外一个复杂组件,它不像 JTable 那样难用,但是也不像 JList 那么容易.使用 JTree 时麻烦的部分是它要求的数据模型. JTree组件的功能来自树的概念,树有分支和叶子 ...

  4. C#和java和android中的NetWorkAdapter,httpRequest,WebView,json,xml

    原文地址:http://blog.csdn.net/intbird C#NetWorkAdapter 20121011.======================================== ...

  5. JTree使用

    package JTree; import java.awt.Component; import javax.swing.Icon; import javax.swing.JTree; import ...

  6. Java Swing的进化

    摘 要:Swing已是一个比较老的工具集了,在美观的用户界面出来之前需要开发很长时间.它缺少一些你在开发富UI时所需的组件.幸运地是,像 Substance,SwingX及Java Look-and_ ...

  7. JTree用法及JTree使用经验总结

    import  java.awt.Dimension; import  java.awt.Color; import  javax.swing.JFrame; import  javax.swing. ...

  8. Jtree (节点的渲染+资源管理器)

    我们的还是自定义的Jtree的类: package jtree.customNode; import java.io.File; import javax.swing.JTree; import ja ...

  9. jtree(选择框)

    jtree一般的用法是: 1. 展示电脑中文件的层次结构,如图所示. 具体的代码: package jtree; import java.io.File; import javax.swing.JTr ...

  10. JTree demo

    JFrame居中方法一:   setLocationRelativeTo(null); 注意:必须在整个frame初始化完成后再加上此语句,否则将显示在屏幕右下角 方法二: private Dimen ...

随机推荐

  1. 性能、成本与 POSIX 兼容性比较: JuiceFS vs EFS vs FSx for Lustre

    JuiceFS 是一款为云环境设计的分布式高性能文件系统.Amazon EFS 易于使用且可伸缩,适用于多种应用.Amazon FSx for Lustre 则是面向处理快速和大规模数据工作负载的高性 ...

  2. SpringBoot定时任务实现数据同步

    业务的需求是,通过中台调用api接口获得,设备数据,要求现实设备数据的同步. 方案一:通过轮询接口的方式执行 pullData() 方法实现数据同步 该方式的原理是先清空之前的所有数据,然后重新插入通 ...

  3. 6.9找回机制接口安全&验证码token接口

    响应包response burp截取拦截,改相应包: 思路:此处应该若是修改密码,先抓到修改成功数据包(截取验证关键字),在替换为需要绕过的数据包,截取response数据包,修改验证成功关键字达到绕 ...

  4. Figma 学习笔记 – Color

    大纲 Figma 的颜色是通过 FIll 实现的 (Fill 还有其它功能比如 fill 图片) 整体大概长这样, 我们一个一个看 颜色和 opacity

  5. docker基础镜像java版本选择和推荐

    背景 在编写dockerfile时,基础镜像要么太大,要么缺少jdk:dockerhub中的openjdk五花八门,不知道选择哪个 解决方案 我在项目中通常选择 openjdk作为基础镜像 FROM ...

  6. 暑假集训PVZ提高模拟9

    没关 exe 让这货挂了一天 UPD:又挂了一晚上,现在被我正义制裁了 A.大众点评 交互红题啊,交互会写,但是忘记判 \(n=1\) 了 这个题交互库函数实现起来还是挺简单的,我 Windows 不 ...

  7. Autodesk 3d Max2020 初始化闪退

    事件起因:给同事安装完 Autodesk 3d Max2020 版本之后,软件初始化就闪退,后来在网上查了资料后解决. 解决办法如下:services.msc --> Autodesk 开头的服 ...

  8. golang的类型转换

    今天我们来说说一个大家每天都在做但很少深入思考的操作--类型转换. 本文索引 一行奇怪的代码 go的类型转换 数值类型之间互相转换 unsafe相关的转换 字符串到byte和rune切片的转换 sli ...

  9. 五行八字在线排盘api接口免费版_json数据格式奥顺互联内部接口

    「八字在线排盘」谁都想知道自己一生中的事业.财运.婚姻.功名.健康.性格.流年运程将是怎样,通过八字排盘,四柱八字排盘会有你想知道的答案.一个人出生的年月时天干地支的排列组合(即八字)就是命.不过仅凭 ...

  10. 初学Linux的可以看看

    Linux常用命令总结 自己简单总结的,希望对大家有帮助吧! 关机/重启 系统信息 文件以及目录 vim文件编辑器 打包压缩 用户和组 权限所有者 RPM YUM mount find 自己简单总结的 ...