// package com.swingswinging.common.ui.tree; package tree; import java.util.*; import javax.swing.tree.*; import org.w3c.dom.*; /** * XMLのDOMを用いて木構造を保持する際に使用するTreeNodeの実装です。DOMのNodeをuserObjectとして保持し、Nodeの親または子のXMLTreeNodeを動的に生成して、JTreeの木構造を組み立てます。 * @version 1.0 2002-12-23 * @author Takashi KOBAYASHI */ public class XMLTreeNode implements TreeNode { /** * userObjectとして格納するDOMのNode */ protected Node node; /** * DOMのNodeをキーにして、全てのXMLTreeNodeが格納されたコレクション */ protected Map elms; private String ign = "\n\r\t "; private String rep = " "; /** * DOMのNodeをuserObjectとして保持するノードを生成します。このコンストラクタを利用した場合、このノードからアクセスされる親または子のノードは、常に新しいインスタンスが生成されます。 * @param node userObjectとして格納するDOMのNode */ public XMLTreeNode( Node node ) { this( node, null ); } /** * DOMのNodeをuserObjectとして保持するノードを生成します。こちらのコンストラクタを利用した場合、このノードからアクセスされる親または子のノードは、最初にコレクションの中を探されます。コレクションにない場合のみ、新しいインスタンスが生成されます。 * @param node userObjectとして格納するDOMのNode * @param elms DOMのNodeをキーにXMLTreeNodeインスタンスを保持するMapのコレクション */ public XMLTreeNode( Node node, Map elms ) { if( node == null ) throw new IllegalArgumentException( "Argument node is Null" ); this.node = node; this.elms = elms; } /** * userObjectとして格納されているDOMのNodeの、childIndexで指定された位置に格納されているの子供のNodeをuserObjectとして格納したXMLTreeNodeを取得します。 * @param childIndex userObjectとして格納されているDOMのNodeの子ノードの位置 * @return childIndex番目のDOMのNodeをuserObjectとして格納したXMLTreeNode */ public TreeNode getChildAt( int childIndex ) { NodeList nodes = node.getChildNodes(); Node key = nodes.item( childIndex ); if( elms != null ) { Object ret = elms.get( key ); if( ret == null ) { ret = new XMLTreeNode( key, elms ); elms.put( key, ret ); } return (TreeNode)ret; } else return new XMLTreeNode( key ); } /** * userObjectとして格納しているDOMのNodeが持つ子ノードの数を取得します。 * @return 子ノードの数 */ public int getChildCount() { NodeList nodes = node.getChildNodes(); return nodes.getLength(); } /** * userObjectとして格納しているDOMのNodeの親のNodeをuserObjectとして持つXMLTreeNodeを返します。 * @return 親のNodeをuserObjectとして格納するXMLTreeNode */ public TreeNode getParent() { Node parent = node.getParentNode(); if( parent == null ) return null; else { if( elms == null ) return new XMLTreeNode( parent ); else { Object ret = elms.get( parent ); if( ret == null ) { ret = new XMLTreeNode( parent, elms ); elms.put( parent, ret ); } return (TreeNode)ret; } } } /** * userObjectとして格納しているDOMのNodeの子供のNodeの中で、引数で渡されたTreeNodeがuserObjectとして格納するDOMのNodeの位置を取得します。 * @param child 子供のNodeをuserObjectとして格納したXMLTreeNode * @return 子供のノードの位置 */ public int getIndex( TreeNode child ) { Node node = ( (XMLTreeNode)child ).getNode(); Enumeration children = children(); int ret = 0; while( children.hasMoreElements() ) { if( ( (XMLTreeNode)children.nextElement() ).getNode().equals( node ) ) return ret; else ret++; } return -1; } /** * そのノードが子供のノードを持つことが許されている場合、trueを返します。 * @return 常にtrue */ public boolean getAllowsChildren() { return true; } /** * userObjectとして格納しているDOMのNodeが、Documet, DocumentFragment, Element, Entity, EntityReference, Notationの場合、常にfalseを返します。それ以外の場合、Node.hasChildNodes()メソッドの結果の逆を返します。 * @return userObjectとして格納しているDOMのNodeが、Documet, DocumentFragment, Element, Entity, EntityReference, Notationの場合、常にfalse。それ以外の場合、Node.hasChildNodes()メソッドの結果の逆。 */ public boolean isLeaf() { boolean ret = !( node.hasChildNodes() ); switch( node.getNodeType() ) { case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: case Node.ELEMENT_NODE: case Node.ENTITY_NODE: case Node.ENTITY_REFERENCE_NODE: case Node.NOTATION_NODE: ret = false; break; default: ret = !( node.hasChildNodes() ); } return ret; } /** * userObjectとして格納しているDOMのNodeの子供のNodeをuserObjectとして格納したXMLTreeNodeに昇順にアクセスする反復子を返します。 * @return 子供のNodeを格納したXMLTreeNodeのEnumeration */ public Enumeration children() { final NodeList nodes = node.getChildNodes(); return new Enumeration() { int index = 0; public boolean hasMoreElements() { if( index >= nodes.getLength() ) return false; else return true; } public Object nextElement() { if( hasMoreElements() ) throw new NoSuchElementException(); Node key = nodes.item( index++ ); Object ret = elms.get( key ); if( ret == null ) { ret = new XMLTreeNode( key, elms ); elms.put( key, ret ); } return ret; } }; } /** * userObjectとして格納するDOMのNodeを取得します。 * @return userObjectとして格納するDOMのNode */ public Node getNode() { return node; } /** * userObjectとして格納するDOMのNodeの中身を表現した文字列を取得します。 * @return userObjectとして格納するDOMのNodeの中身を表現した文字列 */ public String toString() { String str; switch( node.getNodeType() ) { case Node.ELEMENT_NODE: str = ( (Element)node ).getTagName(); if( node.hasAttributes() ) { str += " [ "; NamedNodeMap attrs = node.getAttributes(); for( int i = 0; i < attrs.getLength(); i++ ) { str += attrs.item( i ).getNodeName() + " = " + attrs.item( i ).getNodeValue() + " , "; } str = str.substring( 0, str.length() - 2 ) + "]"; } break; case Node.CDATA_SECTION_NODE: case Node.COMMENT_NODE: case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: str = node.getNodeName(); break; case Node.TEXT_NODE: str = node.getNodeName() + ": " + validate( node.getNodeValue() ); break; case Node.DOCUMENT_TYPE_NODE: str = "#DOCTYPE" + ": " + node.getNodeName(); break; default: str = toString(); } return str; } /** * 引数で渡された文字列に含まれる改行文字やタブ文字の連続を1つの空白文字に置き換えます。userObjectとして格納するDOMのNodeがTextである場合に、toString()メソッド内から呼び出されます。 * @param in 置換前の文字列 * @return 改行文字やタブ文字の連続を1つの空白文字に置換した後の文字列 */ protected String validate( String in ) { StringBuffer out = new StringBuffer(); char c; boolean isBeforeRep = false; for( int i = 0; i < in.length(); i++ ) { c = in.charAt( i ); if( ign.indexOf( (int)c ) != -1 ) { if( ! isBeforeRep ) { out.append( rep ); isBeforeRep = true; } } else { out.append( c ); isBeforeRep = false; } } return out.toString(); } }