/*
*-------------------
* The AbstractTreeTableModel.java is part of ASH Viewer
*-------------------
*
* ASH Viewer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ASH Viewer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ASH Viewer. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (c) 2009, Alex Kardapolov, All rights reserved.
*
*/
package org.ash.history.treetable;
import java.util.EventListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.treetable.TreeTableModel;
// There is no javax.swing.tree.AbstractTreeModel; There ought to be one.
/**
* AbstractTreeTableModel provides an implementation of
* {@link org.ash.history.treetable.TreeTableModel} as a convenient starting
* point in defining custom data models for {@link org.jdesktop.swingx.JXTreeTable}.
*
* @author Ramesh Gupta
*/
public abstract class AbstractTreeTableModel implements TreeTableModel {
/**
* Value returned by {@link org.ash.history.treetable.TreeTableModel#getColumnClass(int) getColumnClass}
* for the {@link org.jdesktop.swingx.JXTreeTable#isHierarchical(int) hierarchical} column.
*/
public final static Class hierarchicalColumnClass = TreeTableModel.class;
/**
* Root node of the model
*/
protected Object root;
/**
* Event listener list
*/
protected EventListenerList listenerList = new EventListenerList();
/**
* Constructs an <code>AbstractTreeTableModel</code> with a null root node
*/
public AbstractTreeTableModel() {
this(null);
}
/**
* Constructs an <code>AbstractTreeTableModel</code> with the specified node
* as the root node.
*
* @param root root node
*/
public AbstractTreeTableModel(Object root) {
this.root = root;
}
/**
* {@inheritDoc}
*/
public Class getColumnClass(int column) {
// Assume that the first column will contain hierarchical nodes.
return column == 0 ? hierarchicalColumnClass : Object.class;
}
/**
* {@inheritDoc}
*/
public String getColumnName(int column) {
return "Column " + column; // Cheap implementation
}
/**
* {@inheritDoc}
*/
public Object getRoot() { // From the TreeNode interface
return root;
}
/**
* Returns the child of <I>parent</I> at index <I>index</I> in the parent's
* child array. <I>parent</I> must be a node previously obtained from
* this data source. This should not return null if <i>index</i>
* is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
* <i>index</i> < getChildCount(<i>parent</i>)).
*
* @param parent a node in the tree, obtained from this data source
* @return the child of <I>parent</I> at index <I>index</I>, or null if the
* specified parent node is not a <code>TreeNode</code>.
*/
public Object getChild(Object parent, int index) {
// meant to be overridden
try {
return ((TreeNode) parent).getChildAt(index);
}
catch (ClassCastException ex) { // not a TreeNode?
return null;
}
}
/**
* Returns the number of children in the specified parent node.
*
* @param parent node whose child count is being requested
* @return the number of children in the specified parent node
*/
public int getChildCount(Object parent) {
// meant to be overridden
try {
return ((TreeNode) parent).getChildCount();
}
catch (ClassCastException ex) { // not a TreeNode?
return 0;
}
}
/**
* {@inheritDoc}
*/
public int getColumnCount() {
// meant to be overridden
return 1; // Cheap (and woefully inadequate) implementation
}
/**
* Returns the index of child in parent.
* If either the parent or child is <code>null</code>, returns -1.
* @param parent a note in the tree, obtained from this data source
* @param child the node we are interested in
* @return the index of the child in the parent, or -1
* if either the parent or the child is <code>null</code>
*/
public int getIndexOfChild(Object parent, Object child) {
if (parent == null || child == null)
return -1;
try {
return ((TreeNode) parent).getIndex((TreeNode) child);
}
catch (ClassCastException ex) { // not a TreeNode?
// This is not called in the JTree's default mode.
// Use a naive implementation.
for (int i = 0; i < getChildCount(parent); i++) {
if (getChild(parent, i).equals(child)) {
return i;
}
}
return -1;
}
}
/**
* {@inheritDoc}
*/
public boolean isCellEditable(Object node, int column) {
// RG: Fix Issue 49 -- Cell not editable, by default.
// Subclasses might override this to return true.
return false;
}
/**
* Returns true if the specified node is a leaf node; false otherwise.
*
* @param node node to test
* @return true if the specified node is a leaf node; false otherwise
*/
public boolean isLeaf(Object node) {
try {
return ((TreeNode) node).isLeaf();
}
catch (ClassCastException ex) { // not a TreeNode?
return getChildCount(node) == 0;
}
}
/**
* Called when value for the item identified by path has been changed.
* If newValue signifies a truly new value the model should
* post a <code>treeNodesChanged</code> event.
*
* @param path path to the node that has changed
* @param newValue the new value from the <code>TreeCellEditor</code>
*/
public void valueForPathChanged(TreePath path, Object newValue) {
/**@todo Implement this javax.swing.tree.TreeModel method*/
}
public void addTreeModelListener(TreeModelListener l) {
listenerList.add(TreeModelListener.class, l);
}
public void removeTreeModelListener(TreeModelListener l) {
listenerList.remove(TreeModelListener.class, l);
}
public TreeModelListener[] getTreeModelListeners() {
return listenerList.getListeners(
TreeModelListener.class);
}
/*
* Notify all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
* @see EventListenerList
*/
protected void fireTreeNodesChanged(Object source, Object[] path,
int[] childIndices, Object[] children) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == TreeModelListener.class) {
// Lazily create the event:
if (e == null)
e = new TreeModelEvent(source, path,
childIndices, children);
((TreeModelListener) listeners[i + 1]).treeNodesChanged(e);
}
}
}
/*
* Notify all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
* @see EventListenerList
*/
protected void fireTreeNodesInserted(Object source, Object[] path,
int[] childIndices, Object[] children) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == TreeModelListener.class) {
// Lazily create the event:
if (e == null)
e = new TreeModelEvent(source, path,
childIndices, children);
((TreeModelListener) listeners[i + 1]).treeNodesInserted(e);
}
}
}
/*
* Notify all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
* @see EventListenerList
*/
protected void fireTreeNodesRemoved(Object source, Object[] path,
int[] childIndices, Object[] children) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == TreeModelListener.class) {
// Lazily create the event:
if (e == null)
e = new TreeModelEvent(source, path,
childIndices, children);
((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e);
}
}
}
/*
* Notify all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
* @see EventListenerList
*/
protected void fireTreeStructureChanged(Object source, Object[] path,
int[] childIndices,
Object[] children) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
TreeModelEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == TreeModelListener.class) {
// Lazily create the event:
if (e == null) {
e = new TreeModelEvent(source, path,
childIndices, children);
}
((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
}
}
}
/**
* Returns an array of all the objects currently registered
* as <code><em>Foo</em>Listener</code>s
* upon this model.
* <code><em>Foo</em>Listener</code>s are registered using the
* <code>add<em>Foo</em>Listener</code> method.
*
* <p>
*
* You can specify the <code>listenerType</code> argument
* with a class literal,
* such as
* <code><em>Foo</em>Listener.class</code>.
* For example, you can query a
* <code>DefaultTreeModel</code> <code>m</code>
* for its tree model listeners with the following code:
*
* <pre>TreeModelListener[] tmls = (TreeModelListener[])(m.getListeners(TreeModelListener.class));</pre>
*
* If no such listeners exist, this method returns an empty array.
*
* @param listenerType the type of listeners requested; this parameter
* should specify an interface that descends from
* <code>java.util.EventListener</code>
* @return an array of all objects registered as
* <code><em>Foo</em>Listener</code>s on this component,
* or an empty array if no such
* listeners have been added
* @exception ClassCastException if <code>listenerType</code>
* doesn't specify a class or interface that implements
* <code>java.util.EventListener</code>
*
* @see #getTreeModelListeners
*
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType) {
return listenerList.getListeners(listenerType);
}
// Left to be implemented in the subclass:
/**
* public Object getChild(Object parent, int index)
* public int getChildCount(Object parent)
* public int getColumnCount()
* public String getColumnName(int column)
* public Object getValueAt(Object node, int column)
* public void setValueAt(Object value, Object node, int column)
*/
}