Here's the short of the long of it: you are viewing the TreeTableNodes as the data backing the model (as a list might back a ListModel). In some circumstances that may work, but it does not do so here. The reason for that is the DefaultTreeTableModel works with TreeTableNodes as though the node were part of the model. You should view DefaultTreeTableModel and TreeTableNodes as a way to place non-hierarchical data into a hierarchical view. In this case, the nodes are not the backing data of the model, but are, in fact, part of it, adapting the non-hierarchical data.
It sounds like you want to have TreeNodes and some type of RowAdapter that provides the mappings to adapter a "hierarchical" structure with a concept of disparate pieces into a tabular-tree view. If thats really the case, then build your "data" into a tree-structure using TreeNodes and the wrap the TreeNodes with TreeTableNodes and do the right thing with the DefaultTreeTableModel.
However, I have always maintained that any time a developer must adapt their underlying data structure into a hierarchical one via the use of TreeNodes that the developer should think twice about what they are doing. Core Swing does everyone a great disservice by only having a DefaultTreeModel that uses nodes. This convinces people incorrectly that they need to adapt their data. So, if your data is already hierarchical, why are you dealing with Tree(Table)Nodes in the first place? If it is not, why are you using a tree view for it?
DefaultTreeTableModel should be considered final. Extending it is a bad idea. TreeTableNode are part of the model, not part of the data. If you return bogus values from the node, do not expect the model to work correctly. You are fighting the models and their design. If you use the API as intended, do you still have problems? If so, I will be happy to take a look at the problem.
Do note that [url=]https://swingx.dev.java.net/issues/show_bug.cgi?id=915Issue 915[/url] discusses the inability of DefaultTreeTableModel to easily configure values for getColumnClass. Otherwise, everything in that model should just work.
Furthermore, there should never be a reason to force the model to fire an update from outside the model. If it is necessary, then something is likely incorrect elsewhere in the code.
The "Model" is - for me - the object structure in the background (in this case: a tree structure made up of subclasses of AbstractMutableTreeTableNode). This object structure can stand on its own, and implements all required data and operations, and is independent of any "View" onto it.
The "View" is a GUI showing some or all aspects of the "Model" mentioned above. In this case I use an instance of JXTreeTable to project the "Model" (a tree) onto the screen.
JXTreeTable needs an implementation of the interface TreeTableModel. However an implementation of TreeTableModel is not suitable as the "Model" because it deals a lot with "Columns" - and the Model has (per definition) no clue about columns: columns belong solely to the "View". The Model has only "Properties".
So I decided to use an implementation of TreeTableModel as a layer between the "View" and the "Model" in order to translate between "Columns" and "Properties":
View <-> Translation <-> Model
JXTreeTable <-> TreeTableModel <-> TreeTableNodes
So that's my architecture, and I find it's a quite reasonable one. If you think I'm fighting the models and their design, then I'd appreciate it very much if you would explained it shortly to me. I think that we have a different conception of the term "Model".
> Furthermore, there should never be a reason to force the model to fire
> an update from outside the model.
Agreed. I have to think it over.
> JXTreeTable needs an implementation of the interface
> TreeTableModel. However an implementation of
> TreeTableModel is not suitable as the "Model" because
> it deals a lot with "Columns" - and the Model has
> (per definition) no clue about columns: columns
> belong solely to the "View".
that's simply wrong (in the context of SwingX which you mention in your definition above) - both treeTableModel and TreeTableNode _do have_ the notion of columns. That's the distinguishing line from a TreeModel and TreeNode. So yes, you are fighting SwingX if you insist on your perspective.
> Hi Karl
> Because when I use DefaultTreeTableModel instead of
> AbstractTreeTableModel, I noticed that the tree nodes
> (which are subclasses of
> AbstractMutableTreeTableNode) are asked to supply the
> column count (the method int getColumnCount() is
> called). This violates the MVC-paradigm to separate
> the data model from the view(s) on this model.
> It's the same with the methods getValueAt(int column)
> and setValueAt(Object aValue, int column), which are
> methods of the interface TreeTableNode. These three
> getValueAt(int column)
> setValueAt(Object aValue, int column)
> do not belong to the TreeTableNode, but to the
> TreeTableModel. Remember that somebody could want to
> have several different views (TreeTableModels) on the
> same tree structure (TreeTableNodes).
All three of those methods are in the TreeTableModel Interface as:
public int getColumnCount();
public Object getValueAt(Object node, int column);
public void setValueAt(Object value, Object node, int column);
It just so happens that the default implementation forwards those method calls to the TreeTableNode implementation. You don't have to do that though. In fact, you don't have to use the TreeTableNode interface at all.
> Now here is another issue: the column widths. When I
> induce a tree view refresh by moving files or
> folders, SOMETIMES the column widths are reset so
> that all columns have the same width. Do you have a
> clue how I can avoid this?
If you fire a tree structure changed event for the root, then JXTreeTable sends a table structure changed event to the TableModel. I'm not sure that is what it should do. IMO, it should only send a table structure changed event to the TableModel if the path identifies a new root.
If you don't want your column structure to change, then call setAutoCreateColumnsFromModel(false) on the JXTreeTable.
No, you're wrong. The method "refresh(TreePath path)" that is called twice in the code snippet above
looks like this:
* Refresh this (path) section of the view tree.
* @param path
public void refresh(TreePath path)
So now that we know what refresh does....
Why are AbstractMutableTreeTableNodes being used with the AbstractTreeTableModel and not the DefaultTreeTableModel?
Because when I use DefaultTreeTableModel instead of AbstractTreeTableModel, I noticed that the tree nodes (which are subclasses of AbstractMutableTreeTableNode) are asked to supply the column count (the method int getColumnCount() is called). This violates the MVC-paradigm to separate the data model from the view(s) on this model.
It's the same with the methods getValueAt(int column) and setValueAt(Object aValue, int column), which are methods of the interface TreeTableNode. These three methods
setValueAt(Object aValue, int column)
do not belong to the TreeTableNode, but to the TreeTableModel. Remember that somebody could want to have several different views (TreeTableModels) on the same tree structure (TreeTableNodes).
But however - now that I use DefaultTreeTableModel instead of AbstractTreeTableModel, no exceptions are thrown any more! That's great!
Now here is another issue: the column widths. When I induce a tree view refresh by moving files or folders, SOMETIMES the column widths are reset so that all columns have the same width. Do you have a clue how I can avoid this?
I understand your concern about the possible MVC issues with TreeTableNode, but I disagree. I see TreeTableNode as an Adapter for converting non-tree-based data into a tree-based structure. As an adapter, it needs to support the methods necessary to properly adapt the underlying data structure to the model. Furthermore, I believe that tree structures are best modeled by a direct subclass of TreeTableModel and not by using TreeTableNodes. These were my beliefs behind designing the models as they currently exist.
As for the width problem, can you provide a small, runnable demo?
I see. I built up the tree structure using subclasses of TreeTableNode and thought of subclasses of TreeTableModel to translate this tree structure into what I show in the GUI. So when the user wants to have another view onto the tree structure, I just create another TreeTableModel class for the other GUI view. So the column information for that specific view is stored in the specific TreeTableModel, without changing the tree structure classes themselves. Hence the TreeTableModel subclasses project the tree structure into the GUI, and that's where I want to put the "column" information in. I separate the tree structure from the views onto it.
Assume I have two different views for the same tree structure (one with 2 columns and one with 5), how would I implement the method getColumnCount() in the tree structure class?
OK then, I'm now creating a simple runnable demo about this column width problem, but I can't attach files to this reply. How then can I supply this demo to you?
OK, here we go. There are two classes, the first one (TreeTableModel) contains a main() that opens a small window with a JXTreeTable and a button. When you klick the button, the columns widths get scrambled.
I hope this works with the code. Oh yes, I'm using swingx 1.6.1.
public class TreeTableModel extends DefaultTreeTableModel
static private JXTreeTable treeTable;
public static void main(String args)
JFrame mainWindow = new JFrame("Main Window");
JPanel buttonsPanel = new JPanel(new FlowLayout());
JButton onButton = new JButton("Refresh Root Node");
public void actionPerformed(ActionEvent e)
TreeTableModel model = (TreeTableModel)treeTable.getTreeTableModel();
model.refresh(treeTable.getPathForRow(0)); // Refresh RootNode
treeTable = new JXTreeTable(new TreeTableModel());
mainWindow.add(new JScrollPane(treeTable), BorderLayout.CENTER);
TableColumnModel columnModel = treeTable.getColumnModel();
mainWindow.setSize(new Dimension(400, 400));
this.root = TreeNode.generateExample();
public boolean isCellEditable(Object node, int column)
public int getColumnCount()
public int getHierarchicalColumn()
public Class> getColumnClass(int column)
case 0: return String.class;
case 1: return Boolean.class;
case 2: return Integer.class;
case 3: return Integer.class;
case 4: return Integer.class;
public String getColumnName(int column)
case 0: return "Name";
case 1: return "is Folder";
case 2: return "Value";
case 3: return "Depth";
case 4: return "Kids";
public Object getValueAt(Object node, int column)
TreeNode treeNode = (TreeNode)node;
case 0: return treeNode.getLabel();
case 1: return !treeNode.isLeaf();
case 2: return treeNode.getValue();
case 3: return treeNode.getDepth();
case 4: return treeNode.getChildCount();
public Object getChild(Object parent, int index)
public int getChildCount(Object parent)
public int getIndexOfChild(Object parent, Object child)
public boolean isLeaf(Object node)
return ((TreeNode)node).getChildCount() == 0;
public void refresh(TreePath path)
public class TreeNode extends AbstractMutableTreeTableNode
private String label;
private Integer value;
TreeNode(TreeNode parent, String label, Integer value)
this.label = label;
this.value = value;
static public TreeNode generateExample()
TreeNode root = new TreeNode(null, "Root", 0);
TreeNode one = new TreeNode(root, "One", 1);
new TreeNode(one, "One.One", 3);
new TreeNode(one, "One.Two", 4);
new TreeNode(one, "One.three", 5);
TreeNode two = new TreeNode(root, "Two", 2);
new TreeNode(two, "Two.One", 6);
new TreeNode(two, "Two.Two", 7);
public String getLabel()
public Integer getValue()
public Integer getDepth()
TreeNode parent = (TreeNode)this.parent;
return (parent == null)? 0: 1 + parent.getDepth();
public int getColumnCount()
public Object getValueAt(int arg0)
public String toString()
return new StringBuilder("[TreeNode:(")
public String treeString(Integer indent)
StringBuilder buf = new StringBuilder(100);
for (int i = 0; i < indent; i++) buf.append("\t");
for (MutableTreeTableNode child: children) buf.append(((TreeNode)child).treeString(indent + 1));
Message was edited by: keksi
Oh boy... somebody removed all those tab characters. Sorry for that.
you can format code by taggin it with [ code ] .... [ /code ] - without the spaces. Please complain to the site admin that this isn't documented anywhere ;-)
Please edit your post and add the tags
Message was edited by: kleopatra
I bet you are not firing model changes in your mutable methods, cf. TreeModelSupport.
Your use of this web site or any of its content or software indicates your agreement to be bound by these Terms of Participation.
Copyright © 2015, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.