001/* 002 The Broad Institute 003 SOFTWARE COPYRIGHT NOTICE AGREEMENT 004 This software and its documentation are copyright (2003-2008) by the 005 Broad Institute/Massachusetts Institute of Technology. All rights are 006 reserved. 007 008 This software is supplied without any warranty or guaranteed support 009 whatsoever. Neither the Broad Institute nor MIT can be responsible for its 010 use, misuse, or functionality. 011*/ 012 013package ca.bc.webarts.widgets.treetable; 014// package org.genomespace.gsui.ui.treetable; 015 016 017import javax.swing.event.EventListenerList; 018import javax.swing.event.TreeModelEvent; 019import javax.swing.event.TreeModelListener; 020import javax.swing.tree.TreeNode; 021import javax.swing.tree.TreePath; 022import javax.swing.tree.DefaultMutableTreeNode; 023 024//import org.genomespace.gsui.ui.table.*; 025//import org.genomespace.gsui.ui.tasks.*; 026 027/** 028 * Description of the Class 029 * 030 * @author Joshua Gould 031 */ 032public abstract class AbstractSortableTreeTableModel implements SortableTreeTableModel 033{ 034 035 protected EventListenerList listenerList = new EventListenerList(); 036 037 public void addTreeModelListener( TreeModelListener l ) 038 { 039 listenerList.add( TreeModelListener.class, l ); 040 } 041 042 public void removeTreeModelListener( TreeModelListener l ) 043 { 044 listenerList.remove( TreeModelListener.class, l ); 045 } 046 047 public void valueForPathChanged( TreePath path, Object newValue ) 048 { 049 050 } 051 052 public abstract void sortOrderChanged( SortEvent e ); 053 054 055 /** 056 * Invoke this method after you've inserted some TreeNodes into node. 057 * childIndices should be the index of the new elements and must be sorted 058 * in ascending order. 059 * 060 * @param node 061 * Description of the Parameter 062 * @param childIndices 063 * Description of the Parameter 064 */ 065 public void nodesWereInserted( TreeNode node, int[] childIndices ) 066 { 067 if ( listenerList != null && node != null && childIndices != null && childIndices.length > 0 ) 068 { 069 int cCount = childIndices.length; 070 Object[] newChildren = new Object[cCount]; 071 072 for ( int counter = 0; counter < cCount; counter++ ) 073 { 074 newChildren[counter] = node.getChildAt( childIndices[counter] ); 075 } 076 077 fireTreeNodesInserted( this, getPathToRoot( node ), childIndices, newChildren ); 078 079 } 080 } 081 082 /** 083 * Invoke this method after you've changed how node is to be 084 * represented in the tree. 085 */ 086 public void nodeChanged( TreeNode node ) 087 { 088 if ( listenerList != null && node != null ) 089 { 090 TreeNode parent = node.getParent(); 091 092 if ( parent != null ) 093 { 094 int anIndex = parent.getIndex( node ); 095 if ( anIndex != -1 ) 096 { 097 int[] cIndexs = new int[1]; 098 099 cIndexs[0] = anIndex; 100 nodesChanged( parent, cIndexs ); 101 } 102 } 103 else if ( node == getRoot() ) 104 { 105 nodesChanged( node, null ); 106 } 107 } 108 } 109 110 /** 111 * Invoke this method after you've removed some TreeNodes from node. 112 * childIndices should be the index of the removed elements and must be 113 * sorted in ascending order. And removedChildren should be the array of the 114 * children objects that were removed. 115 * 116 * @param node 117 * Description of the Parameter 118 * @param childIndices 119 * Description of the Parameter 120 * @param removedChildren 121 * Description of the Parameter 122 */ 123 public void nodesWereRemoved( TreeNode node, int[] childIndices, Object[] removedChildren ) 124 { 125 if ( node != null && childIndices != null ) 126 { 127 fireTreeNodesRemoved( this, getPathToRoot( node ), childIndices, removedChildren ); 128 } 129 } 130 131 /** 132 * Invoke this method after you've changed how the children identified by 133 * childIndicies are to be represented in the tree. 134 * 135 * @param node 136 * Description of the Parameter 137 * @param childIndices 138 * Description of the Parameter 139 */ 140 public void nodesChanged( TreeNode node, int[] childIndices ) 141 { 142 if ( node != null ) 143 { 144 if ( childIndices != null ) 145 { 146 int cCount = childIndices.length; 147 148 if ( cCount > 0 ) 149 { 150 Object[] cChildren = new Object[cCount]; 151 152 for ( int counter = 0; counter < cCount; counter++ ) 153 { 154 cChildren[counter] = node.getChildAt( childIndices[counter] ); 155 } 156 fireTreeNodesChanged( this, getPathToRoot( node ), childIndices, cChildren ); 157 } 158 } 159 else if ( node == getRoot() ) 160 { 161 fireTreeNodesChanged( this, getPathToRoot( node ), null, null ); 162 } 163 } 164 } 165 166 /** 167 * Invoke this method if you've totally changed the children of node and its 168 * childrens children... This will post a treeStructureChanged event. 169 * 170 * @param node 171 * Description of the Parameter 172 */ 173 public void nodeStructureChanged( TreeNode node ) 174 { 175 if ( node != null ) 176 { 177 fireTreeStructureChanged( this, getPathToRoot( node ), null, null ); 178 } 179 } 180 181 /* 182 * Notify all listeners that have registered interest for notification on 183 * this event type. The event instance is lazily created using the 184 * parameters passed into the fire method. 185 * 186 * @see EventListenerList 187 */ 188 protected void fireTreeNodesChanged( Object source, Object[] path, int[] childIndices, Object[] children ) 189 { 190 // Guaranteed to return a non-null array 191 try 192 { 193 Object[] listeners = listenerList.getListenerList(); 194 TreeModelEvent e = null; 195 // Process the listeners last to first, notifying 196 // those that are interested in this event 197 for ( int i = listeners.length - 2; i >= 0; i -= 2 ) 198 { 199 if ( listeners[i] == TreeModelListener.class ) 200 { 201 // Lazily create the event: 202 if ( e == null ) 203 { 204 e = new TreeModelEvent( source, path, childIndices, children ); 205 } 206 ( ( TreeModelListener ) listeners[ i + 1] ).treeNodesChanged( e ); 207 } 208 } 209 } 210 catch ( Throwable t ) 211 { 212 t.printStackTrace(); 213 } 214 } 215 216 /* 217 * Notify all listeners that have registered interest for notification on 218 * this event type. The event instance is lazily created using the 219 * parameters passed into the fire method. 220 * 221 * @see EventListenerList 222 */ 223 protected void fireTreeNodesInserted( Object source, Object[] path, int[] childIndices, Object[] children ) 224 { 225 try 226 { 227 // Guaranteed to return a non-null array 228 Object[] listeners = listenerList.getListenerList(); 229 TreeModelEvent e = null; 230 // Process the listeners last to first, notifying 231 // those that are interested in this event 232 for ( int i = listeners.length - 2; i >= 0; i -= 2 ) 233 { 234 if ( listeners[i] == TreeModelListener.class ) 235 { 236 // Lazily create the event: 237 if ( e == null ) 238 { 239 e = new TreeModelEvent( source, path, childIndices, children ); 240 } 241 ( ( TreeModelListener ) listeners[ i + 1] ).treeNodesInserted( e ); 242 } 243 } 244 } 245 catch ( Throwable t ) 246 { 247 t.printStackTrace(); 248 } 249 } 250 251 /* 252 * Notify all listeners that have registered interest for notification on 253 * this event type. The event instance is lazily created using the 254 * parameters passed into the fire method. 255 * 256 * @see EventListenerList 257 */ 258 protected void fireTreeNodesRemoved( Object source, Object[] path, int[] childIndices, Object[] children ) 259 { 260 // Guaranteed to return a non-null array 261 try 262 { 263 Object[] listeners = listenerList.getListenerList(); 264 TreeModelEvent e = null; 265 // Process the listeners last to first, notifying 266 // those that are interested in this event 267 for ( int i = listeners.length - 2; i >= 0; i -= 2 ) 268 { 269 if ( listeners[i] == TreeModelListener.class ) 270 { 271 // Lazily create the event: 272 if ( e == null ) 273 { 274 e = new TreeModelEvent( source, path, childIndices, children ); 275 } 276 ( ( TreeModelListener ) listeners[ i + 1] ).treeNodesRemoved( e ); 277 } 278 } 279 } 280 catch ( Throwable t ) 281 { 282 t.printStackTrace(); 283 } 284 } 285 286 /* 287 * Notify all listeners that have registered interest for notification on 288 * this event type. The event instance is lazily created using the 289 * parameters passed into the fire method. 290 * 291 * @see EventListenerList 292 */ 293 protected void fireTreeStructureChanged( Object source, Object[] path, int[] childIndices, Object[] children ) 294 { 295 try 296 { 297 // Guaranteed to return a non-null array 298 Object[] listeners = listenerList.getListenerList(); 299 TreeModelEvent e = null; 300 // Process the listeners last to first, notifying 301 // those that are interested in this event 302 for ( int i = listeners.length - 2; i >= 0; i -= 2 ) 303 { 304 if ( listeners[i] == TreeModelListener.class ) 305 { 306 // Lazily create the event: 307 if ( e == null ) 308 { 309 e = new TreeModelEvent( source, path, childIndices, children ); 310 } 311 ( ( TreeModelListener ) listeners[ i + 1] ).treeStructureChanged( e ); 312 } 313 } 314 } 315 catch ( Throwable t ) 316 { 317 t.printStackTrace(); 318 } 319 } 320 321 protected void fireTreeStructureChanged( Object source, Object[] path ) 322 { 323 // int[] childIndices, Object[] children) { 324 // Guaranteed to return a non-null array 325 try 326 { 327 Object[] listeners = listenerList.getListenerList(); 328 TreeModelEvent e = null; 329 // Process the listeners last to first, notifying 330 // those that are interested in this event 331 for ( int i = listeners.length - 2; i >= 0; i -= 2 ) 332 { 333 if ( listeners[i] == TreeModelListener.class ) 334 { 335 // Lazily create the event: 336 if ( e == null ) 337 { 338 e = new TreeModelEvent( source, path ); 339 // childIndices, children); 340 } 341 ( ( TreeModelListener ) listeners[ i + 1] ).treeStructureChanged( e ); 342 } 343 } 344 } 345 catch ( Throwable t ) 346 { 347 t.printStackTrace(); 348 } 349 } 350 351 public void setValueAt( Object value, Object node, int column ) 352 { 353 354 } 355 356 /** 357 * Builds the parents of node up to and including the root node, where the 358 * original node is the last element in the returned array. The length of 359 * the returned array gives the node's depth in the tree. 360 * 361 * @param aNode 362 * the TreeNode to get the path for 363 * @return The pathToRoot value 364 */ 365 public Object[] getPathToRoot( TreeNode aNode ) 366 { 367 return getPathToRoot( aNode, 0 ); 368 } 369 370 /** 371 * Returns an array of all the tree model listeners registered on this 372 * model. 373 * 374 * @return all of this model's <code>TreeModelListener</code> s or an 375 * empty array if no tree model listeners are currently registered 376 * @see #addTreeModelListener 377 * @see #removeTreeModelListener 378 * @since 1.4 379 */ 380 public TreeModelListener[] getTreeModelListeners() 381 { 382 return ( TreeModelListener[] ) listenerList.getListeners( TreeModelListener.class ); 383 } 384 385 public boolean isCellEditable( Object node, int column ) 386 { 387 return false; 388 } 389 390 public int getChildCount( Object parent ) 391 { 392 return ( ( DefaultMutableTreeNode ) parent ).getChildCount(); 393 } 394 395 public int getIndexOfChild( Object parent, Object child ) 396 { 397 return ( ( DefaultMutableTreeNode ) parent ) .getIndex( ( DefaultMutableTreeNode ) child ); 398 } 399 400 public abstract Class getColumnClass( int column ); 401 402 public abstract Object getRoot(); 403 404 public boolean isLeaf( Object node ) 405 { 406 return ( ( DefaultMutableTreeNode ) node ).isLeaf(); 407 } 408 409 public Object getChild( Object parent, int index ) 410 { 411 return ( ( DefaultMutableTreeNode ) parent ).getChildAt( index ); 412 } 413 414 public abstract int getColumnCount(); 415 416 public abstract String getColumnName( int column ); 417 418 public abstract Object getValueAt( Object node, int column ); 419 420 /** 421 * Builds the parents of node up to and including the root node, where the 422 * original node is the last element in the returned array. The length of 423 * the returned array gives the node's depth in the tree. 424 * 425 * @param aNode 426 * the TreeNode to get the path for 427 * @param depth 428 * an int giving the number of steps already taken towards the 429 * root (on recursive calls), used to size the returned array 430 * @return an array of TreeNodes giving the path from the root to the 431 * specified node 432 */ 433 protected Object[] getPathToRoot( TreeNode aNode, int depth ) 434 { 435 Object[] retNodes; 436 // This method recurses, traversing towards the root in order 437 // size the array. On the way back, it fills in the nodes, 438 // starting from the root and working back to the original node. 439 440 /* 441 * Check for null, in case someone passed in a null node, or they passed 442 * in an element that isn't rooted at root. 443 */ 444 if ( aNode == null ) 445 { 446 if ( depth == 0 ) 447 { 448 return null; 449 } 450 else 451 { 452 retNodes = new Object[depth]; 453 } 454 } 455 else 456 { 457 depth++; 458 if ( aNode == getRoot() ) 459 { 460 retNodes = new Object[depth]; 461 } 462 else 463 { 464 retNodes = getPathToRoot( aNode.getParent(), depth ); 465 } 466 retNodes[ retNodes.length - depth] = aNode; 467 } 468 return retNodes; 469 } 470 471} 472