XmlTreeModel.java
/*
* Copyright (c) 2003-2004 by Woehrmann Softwareentwicklung
*
* Woehrmann Softwareentwicklung (hereinafter referred to as "Developer")
* grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that Licensee does not utilize the software in a manner
* which is disparaging to the Developer.
*
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. THE DEVELOPER SHALL NOT BE LIABLE
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL THE DEVELOPER
* BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY
* TO USE SOFTWARE, EVEN IF THE DEVELOPER HAS BEEN ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES.
*
*/
import java.util.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import org.w3c.dom.*;
public class XmlTreeModel implements TreeModel
{ private XmlTreeNodeFilter treeNodeFilter;
private Vector treeModelListeners;
private Node rootNode;
/**
* Creates a new <code>XmlTreeModel</code>.
*
* @param rootNode as the root-node of the XML structure.
*/
public XmlTreeModel(Node rootNode)
{ this(rootNode, null);
}
/**
* Creates a new <code>XMLDocumentTree</code>.
*
* @param rootNode as the root-node of the XML structure.
* @param filter that defines what tree nodes should be visible or not.
*/
public XmlTreeModel(Node rootNode, XmlTreeNodeFilter filter)
{ this.rootNode = rootNode;
treeModelListeners = new Vector();
setTreeNodeFilter(filter);
}
/**
* Sets the tree node filter for this tree model.
*
* @param filter that defines what tree nodes should be visible or not.
*/
public void setTreeNodeFilter(XmlTreeNodeFilter filter)
{ this.treeNodeFilter = filter;
}
//=============================================================================
// Implementing the TreeModel-Interface
//-----------------------------------------------------------------------------
/**
* Adds a listener for the TreeModelEvent posted after the tree changes.
*
* @param listener as the TreeModelListener.
*/
public void addTreeModelListener(TreeModelListener listener)
{ if (listener != null && !treeModelListeners.contains(listener))
{ treeModelListeners.addElement(listener);
}
}
/**
* Removes a listener from the listeners list.
*
* @param listener as the TreeModelListener.
*/
public void removeTreeModelListener(TreeModelListener listener)
{ if (listener != null) treeModelListeners.removeElement(listener);
}
/**
* Returns the root node of the tree.
*/
public Object getRoot()
{ return rootNode;
}
/**
* Returns false if node has visible childs, else true.
*/
public boolean isLeaf(Object node)
{ return getVisibleChildCount((Node)node) == 0;
}
/**
* Returns the child XMLElement of parent XMLElement at index index.
*/
public Object getChild(Object parent, int index)
{ return getVisibleChildAt((Node)parent, index);
}
/**
* Returns the number of children of parent.
*/
public int getChildCount(Object parent)
{ return getVisibleChildCount((Node)parent);
}
/**
* Returns the number of children of parent.
*/
public int getIndexOfChild(Object parent, Object child)
{ return getIndexOfVisibleChild((Node)parent, (Node)child);
}
/**
* Not implemented in this TreeModel.
*/
public void valueForPathChanged(TreePath path, Object newValue)
{ /* not implemented */
}
//=============================================================================
// Internal filter support ...
//-----------------------------------------------------------------------------
private boolean isVisibleXmlTreeNode(Node node)
{ return (treeNodeFilter == null) ? true : treeNodeFilter.isVisibleXmlTreeNode(node);
}
private Node getVisibleChildAt(Node parentNode, int index)
{ NodeList childNodes = parentNode.getChildNodes();
for (int i = 0, visibleIndex = 0, n = childNodes.getLength(); i < n; i++)
{ Node childNode = childNodes.item(i);
if (!isVisibleXmlTreeNode(childNode)) continue;
if (index == visibleIndex) return childNode;
visibleIndex++;
}
return null;
}
private int getVisibleChildCount(Node parentNode)
{ NodeList childNodes = parentNode.getChildNodes();
int visibleChildCount = 0;
for (int i = 0, n = childNodes.getLength(); i < n; i++)
{ if (isVisibleXmlTreeNode(childNodes.item(i))) visibleChildCount++;
}
return visibleChildCount;
}
private int getIndexOfVisibleChild(Node parentNode, Node childNode)
{ NodeList childNodes = parentNode.getChildNodes();
for (int i = 0, visibleChildCount = 0, n = childNodes.getLength(); i < n; i++)
{ Node checkNode = childNodes.item(i);
if (isVisibleXmlTreeNode(checkNode))
{ if (checkNode.equals(childNode)) return visibleChildCount;
visibleChildCount++;
}
}
return -1;
}
//=============================================================================
// Private Tree-Model Event support ...
//-----------------------------------------------------------------------------
private void fireTreeNodesChanged(TreeModelEvent e)
{ for (int i = 0, n = treeModelListeners.size(); i < n; i++)
{ ((TreeModelListener)treeModelListeners.elementAt(i)).treeNodesChanged(e);
}
}
private void fireTreeNodesInserted(TreeModelEvent e)
{ for (int i = 0, n = treeModelListeners.size(); i < n; i++)
{ ((TreeModelListener)treeModelListeners.elementAt(i)).treeNodesInserted(e);
}
}
private void fireTreeNodesRemoved(TreeModelEvent e)
{ for (int i = 0, n = treeModelListeners.size(); i < n; i++)
{ ((TreeModelListener)treeModelListeners.elementAt(i)).treeNodesRemoved(e);
}
}
private void fireTreeStructureChanged(TreeModelEvent e)
{ for (int i = 0, n = treeModelListeners.size(); i < n; i++)
{ ((TreeModelListener)treeModelListeners.elementAt(i)).treeStructureChanged(e);
}
}
private void fireTreeNodeInserted(Node parentNode, Node childNode)
{ TreeModelEvent e = makeTreeModelChangeEvent(parentNode, childNode);
fireTreeNodesInserted(e);
}
private TreeModelEvent makeTreeModelChangeEvent(Node parentNode, Node childNode)
{ return new TreeModelEvent(
this,
getPathToRoot( parentNode ),
new int[] { getIndexOfVisibleChild(parentNode, childNode) },
new Object[] { childNode }
);
}
//=============================================================================
// Tree-Path and -Node support ...
//-----------------------------------------------------------------------------
/**
* Provides an array of Nodes as path from root to node.
*
* @param node as the path delimited Node
* @return Object[] as path where Object[0] is the root node and Object[size - 1] is the given node.
*/
public Object[] getPathToRoot(Node node)
{ Vector pathNodes = new Vector();
Node parentNode = node;
while (parentNode != null)
{ pathNodes.add(0, parentNode);
if (parentNode.equals(rootNode)) break;
parentNode = parentNode.getParentNode();
}
return (parentNode.equals(rootNode)) ? pathNodes.toArray() : new Object[0];
}
/**
* Returns the number of siblings of child (inlc. child). This is the number of childs of childs parent.
*
* @return number of siblings or -1 if child is the root element.
*/
public int getSiblingsCount(Node child)
{ Node parent = child.getParentNode();
if (parent != null) return getChildCount(parent);
return -1;
}
//=============================================================================
// Tree-Event support ...
//-----------------------------------------------------------------------------
/**
* Notifies all listeners that have registered interest for notification on changes.
*
* @param changeNode as Node whose underlying structure has changed.
*/
public void fireTreeStructureChanged(Node changedNode)
{ fireTreeStructureChanged(new TreeModelEvent(this, getPathToRoot(changedNode)));
}
/**
* Notifies all listeners that have registered interest for notification on changes.
*
* @param treeNode as Node whose content has changed.
*/
public void fireTreeNodeChanged(Node treeNode)
{ fireTreeNodesChanged(new TreeModelEvent(this, getPathToRoot(treeNode)));
}
//=============================================================================
//Tree-Manipulation-Support
//-----------------------------------------------------------------------------
/**
* Adds a childNode to a parentNode.<br>
* Notifies all listeners that have registered interest.
*
* @param parentNode as parent of the child Node.
* @param childNode as Node to be added.
*/
public void addXmlTreeNode(Node parentNode, Node childNode)
{ if (parentNode != null && childNode != null)
{ parentNode.appendChild(childNode);
fireTreeNodeInserted (parentNode, childNode);
}
}
/**
* Adds a node before a given successor.<br>
* Notifies all listeners that have registered interest.
*
* @param node as Node to be added.
* @param successor as Node after the to be added node
*/
public void addXmlTreeNodeBefore(Node node, Node successor)
{ if (node != null)
{ Node parentNode = node.getParentNode();
if (parentNode != null)
{ if (successor == null) // means add to the end
{ addXmlTreeNode(parentNode, node);
}
else // add before successor
{ parentNode.insertBefore(node, successor);
fireTreeNodeInserted(parentNode, node);
}
}
}
}
/**
* Removes a node from the tree.
*
* @param node as Node to be removed.
*/
public void removeXmlTreeNode(Node node)
{ if (node != null)
{ Node parentNode = node.getParentNode();
if (parentNode != null)
{ TreeModelEvent removeEvent = makeTreeModelChangeEvent(parentNode, node);
parentNode.removeChild(node);
fireTreeNodesRemoved(removeEvent);
}
}
}
}