public class TreeTableModelExample extends AbstractTreeTableModel implements TreeTableModel{
private Node root;
private int row;
private String[] columNames;
private String[] methodNames;
private String[] setterMethodNames;
private Class[] cTypes;
public TreeTableModelExample(Node root, int row, String[] columnNames,String[] methodNames,String[] setterMethodNames, Class[] cTypes){
super(root);
this.root = root;
this.row = row;
this.columNames = columnNames;
this.methodNames = methodNames;
this.setterMethodNames = setterMethodNames;
this.cTypes = cTypes;
}
/**
* TreeModel method to return the number of children of a particular
* node. Since <code>node</code> is a TreeNode, this can be answered
* via the TreeNode method <code>getChildCount</code>.
*/
public int getChildCount(Object parent) {
Node node = (Node)parent;
NamedNodeMap attrs = node.getAttributes();
int attrCount = attrs!=null ? attrs.getLength() : 0;
NodeList children = node.getChildNodes();
int childCount = children!=null ? children.getLength() : 0;
return (attrCount + childCount)/2;
}
/**
* TreeModel method to locate a particular child of the specified
* node. Since <code>node</code> is a TreeNode, this can be answered
* via the TreeNode method <code>getChild</code>.
*/
public Object getChild(Object parent, int index) {
Node node = (Node)parent;
NamedNodeMap attrs = node.getAttributes();
int attrCount = attrs!=null ? attrs.getLength() : 0;
if(index<attrCount)
return attrs.item(index);
NodeList children = node.getChildNodes();
return children.item((index - attrCount)*2+1);
}
/**
* TreeModel method to determine if a node is a leaf.
* Since <code>node</code> is a TreeNode, this can be answered
* via the TreeNode method <code>isLeaf</code>.
*/
public boolean isLeaf(Object node) {
return getChildCount(node)==0;
}
//
// The TreeTable interface.
//
/**
* Returns the number of column names passed into the constructor.
*/
public int getColumnCount() {
return row;
}
/**
* Returns the column name passed into the constructor.
*/
public String getColumnName(int column) {
return columNames[column];
}
/**
* Returns the column class for column <code>column</code>. This
* is set in the constructor.
*/
public Class getColumnClass(int column) {
if (cTypes == null || column < 0 || column >= cTypes.length) {
return null;
}
return cTypes[column];
}
/**
* Returns the value for the column <code>column</code> and object
* <code>node</code>. The return value is determined by invoking
* the method specified in constructor for the passed in column.
*/
public Object getValueAt(Object node, int column) {
Node myNode=(Node) node;
try {
switch (column) {
case 0:
return myNode.getNodeName();
case 1:
return myNode.getNodeValue();
case 2:
return myNode.getParentNode();
}
}
catch (SecurityException se) {}
return null;
}
/**
* Returns true if there is a setter method name for column
* <code>column</code>. This is set in the constructor.
*/
public boolean isCellEditable(Object node, int column) {
return ((setterMethodNames != null && setterMethodNames[column] != null));
}
/**
* Sets the value to <code>aValue</code> for the object
* <code>node</code> in column <code>column</code>. This is done
* by using the setter method name, and coercing the passed in
* value to the specified type.
*/
// Note: This looks up the methods each time! This is rather inefficient;
// it should really be changed to cache matching methods/constructors
// based on <code>node</code>'s class, and <code>aValue</code>'s class.
public void setValueAt(Object aValue, Object node, int column) {
if(column == 0){
return;
}
boolean found = false;
try {
// We have to search through all the methods since the
// types may not match up.
Method[] methods = node.getClass().getMethods();
for (int counter = methods.length - 1; counter >= 0; counter--) {
if (methods[counter].getName().equals
(setterMethodNames[column]) && methods[counter].
getParameterTypes() != null && methods[counter].
getParameterTypes().length == 1) {
// We found a matching method
Class param = methods[counter].getParameterTypes()[0];
if (!param.isInstance(aValue)) {
// Yes, we can use the value passed in directly,
// no coercision is necessary!
if (aValue instanceof String &&
((String)aValue).length() == 0) {
// Assume an empty string is null, this is
// probably bogus for here.
aValue = null;
}
else {
// Have to attempt some sort of coercision.
// See if the expected parameter type has
// a constructor that takes a String.
Constructor cs = param.getConstructor
(new Class[] { String.class });
if (cs != null) {
aValue = cs.newInstance(new Object[]
{ aValue });
}
else {
aValue = null;
}
}
}
// null either means it was an empty string, or there
// was no translation. Could potentially deal with these
// differently.
methods[counter].invoke(node, new Object[] { aValue });
found = true;
break;
}
}
} catch (Throwable th) {
System.out.println("exception: " + th);
}
if (found) {
// The value changed, fire an event to notify listeners.
TreeNode parent = ((TreeNode)node).getParent();
fireTreeNodesChanged(this, getPathToRoot(parent),
new int[] { getIndexOfChild(parent, node) },
new Object[] { node });
}
}
/**
* Builds the parents of the node up to and including the root node,
* where the original node is the last element in the returned array.
* The length of the returned array gives the node's depth in the
* tree.
*
* @param aNode the TreeNode to get the path for
* @param an array of TreeNodes giving the path from the root to the
* specified node.
*/
public TreeNode[] getPathToRoot(TreeNode aNode) {
return getPathToRoot(aNode, 0);
}
/**
* Builds the parents of the node up to and including the root node,
* where the original node is the last element in the returned array.
* The length of the returned array gives the node's depth in the
* tree.
*
* @param aNode the TreeNode to get the path for
* @param depth an int giving the number of steps already taken towards
* the root (on recursive calls), used to size the returned array
* @return an array of TreeNodes giving the path from the root to the
* specified node
*/
private TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
TreeNode[] retNodes;
// This method recurses, traversing towards the root in order
// size the array. On the way back, it fills in the nodes,
// starting from the root and working back to the original node.
/* Check for null, in case someone passed in a null node, or
they passed in an element that isn't rooted at root. */
if(aNode == null) {
if(depth == 0)
return null;
else
retNodes = new TreeNode[depth];
}
else {
depth++;
if(aNode == root)
retNodes = new TreeNode[depth];
else
retNodes = getPathToRoot(aNode.getParent(), depth);
retNodes[retNodes.length - depth] = aNode;
}
return retNodes;
}
}