Skip to main content

Model extensions

40 replies [Last post]
rameshgupta
Offline
Joined: 2004-06-04

The existing Swing TreeModel interface is incomplete, lacking important methods, such as those for inserting and removing nodes. As a result, developers either have to extend the interface and take the responsibility of model management upon themselves, or they have to forget the interface, and limit themselves to DefaultTreeModel. Even in the latter case, the onus for calling DefaultTreeModel methods, such as nodesWereInserted and nodesWererRemoved, lies on developers, unless they code directly to the DefaultTreeModel API (calling insertNodeInto or removeNodeFromParent). Unfortunately, by doing so, they foreclose their ability to use a custom tree model class that does not extend DefaultTreeModel.

What do people think of an extended tree model interface (e.g., MutableTreeModel) that fixes these problems? If done right, it should make it possible for people to code only to the interface, not to the actual implementation.

The interface would be backward compatible in that any code that expects a TreeModel would accept MutableTreeModel. Does it make sense to pursue model extensions like this?

Ramesh

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Scott Violet

jdnc-interest@javadesktop.org wrote:
>>What do people think of an extended tree model
>>interface (e.g., [b]MutableTreeModel[/b]) that fixes
>>these problems? If done right, it should make it
>>possible for people to code only to the interface,
>>not to the actual implementation.
>
>
> [i]My Incoherent Ramblings[/i]
> I've often wondered why we don't have a MutableTableModel,
> MutableTreeModel, or MutableListModel. As Scott points out, mutability
> is added to the abstract classes rather than the interfaces, but I don't
> know why this has to be.

I definitely would not want the current models to be mutable. Often
times I'll only create a read-only model, in which case having to
implement the mutable, or isSupported style of methods would be painful.
But it doesn't really matter, the current set of interfaces is locked
and can't change.

> As a matter of principle I prefer coding to the
> interfaces rather than to implementations, though as a matter of
> practicality I've never known it to make much difference.

I generally do as well. For example, I'll have List instead of
ArrayList. When I stray from the default models I often create
one offs with a specific API.

> Perhaps the original intent was that the models would be as simple as
> possible, and since JList, JTree and JTable don't provide gestures for
> adding & removing items anyway, why should the models? However, my
> opinion is that these components ought to provide such gestures/actions,
> allowing users to add/remove items (like the .NET Grid control). If
> these components contain such actions/gestures, then the models ought to
> support mutability.

I agree that making it easy to add these gestures would be nice.

-Scott

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Richard Bair

>>I've often wondered why we don't have a MutableTableModel,
>>MutableTreeModel, or MutableListModel. As Scott points out, mutability
>>is added to the abstract classes rather than the interfaces, but I don't
>>know why this has to be.
>
>I definitely would not want the current models to be mutable. Often times
>I'll only create a read-only model, in which case having to implement the
>mutable, or isSupported style of methods would be painful. But it doesn't
>really matter, the current set of interfaces is locked and can't change.

I'm not suggesting changing the current interfaces, certainly. But it seems
like extending them is always a possibility. It does mean doing an
instanceof check within the view components, though.

Richard

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Scott Violet

Richard Bair wrote:
>>> I've often wondered why we don't have a MutableTableModel,
>>> MutableTreeModel, or MutableListModel. As Scott points out, mutability
>>> is added to the abstract classes rather than the interfaces, but I don't
>>> know why this has to be.
>>
>>
>> I definitely would not want the current models to be mutable. Often
>> times I'll only create a read-only model, in which case having to
>> implement the mutable, or isSupported style of methods would be
>> painful. But it doesn't really matter, the current set of interfaces
>> is locked and can't change.
>
>
> I'm not suggesting changing the current interfaces, certainly.

;)

> But it
> seems like extending them is always a possibility. It does mean doing an
> instanceof check within the view components, though.

Yep, I think that's the only way.

-Scott

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

It would be interesting to sketch out some use cases, methinks.

For the user, a nice-to-have for lists, trees and tables are the basic
UI operations we have in similar apps, built-in: cut, copy, paste and
reorder elements, using keyboard, menu and drag-and-drop.

For the developer, it would be nice if it were easier to mock-up data
for tables, at least. I personally hate using the arrays approach and
the vector approach. My own preference for JXTable is to use a
DataTable not bound to a SQL source, but either created in code or
loaded from a text file, for example, when building the prototype for
an app.

Whether you can move rows around, or cut/copy/paste seems to depend on
the underlying model allowing it, which may mean something like a
known key existing per row, and a way to create new keys. Using the
DataTable as a backing view would support that automatically.

Just 0.02
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Scott Violet

Patrick,

I was just thinking about this. There are actually two places that
could use Mutable*: drag and drop as well as cut-copy-paste.

-Scott

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Patrick,
>
> I finally got the filter and deferred node loading
> code to work, though it took quite a bit of hacking
> ;-) In places, I didn't really understand your code,
> so I used my best guess, and might have accidently
> second-guessed you. The fixed up code is messy and
> unwieldy right now. I'll scrub it and post it
> tomorrow, I hope, but definitely by Friday.
>
> Overall, I like your approach!
>
> Ramesh

Patrick,

Sorry, I didn't get to this on Friday, as promised :-(. Too much stuff happening. As I mentioned earlier, the interface and class names are still shifting. To avoid CVS thrash, I uploaded the files to https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI...

You might want to start with the source for FileTreeBuilderDemo, which uses an instance of DeferredFileTreeBuilder to build the model lazily. EagerFileTreeBuilder, an alternative builder, will populate the whole model eagerly. Looks quite different from the original approach, though.

> > I'm curious to see how this shakes out. Just my .02,
> > there definitely must be support for dynamically
> > building the tree model when nodes are expanded in
> > the view. If this mechanism doesn't support it,
> > some mechanism must (somewhere down the road).
>
> +1
>
> The Oracle ADF framework currently does this with a
> custom NodeBinding that builds the nodes from a data
> view. The child nodes aren't fetched until the node
> is expanded.
>
> Erik

Erik, I don't know how the ADF framework does this. I tried googling for it, but nothing interesting turned up. Would be great if someone familiar with that could compare the two approaches.

Ramesh

Patrick Wright

Hi Ramesh

OK, I downloaded and installed this locally and ran the demo. The code
seems simpler and cleaner now, good work!

What I need to figure out is what I mentioned earlier in the thread,
which is how to exclude directories (if we are building from a
directory tree) where there are no child or descendant files that
match the filter. I'm not sure how to do that with this approach. I
think we would need the EagerFileTreeBuilder to run listFiles() first,
then populate(), then add the nodes--in other words, recurse the tree
entirely, and add nodes from the bottom up. Sorry, that sounds
confusing as I write it, need to mock this up.

I'm thinking that we might have a BuilderFilter instead of a
FileFilter? to generalize the filtering process one step...need to
think about that. For files, the BuilderFilter would contain a
FileFilter, put there could be filters for other types of nodes as
well.

OK, making progress, nice work!
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Hi Ramesh
>
> OK, I downloaded and installed this locally and ran
> the demo. The code seems simpler and cleaner now,
> good work!
>
> What I need to figure out is what I mentioned earlier
> in the thread, which is how to exclude directories
> (if we are building from a directory tree) where there
> are no child or descendant files that match the filter.
> I'm not sure how to do that with this approach.
> I think we would need the EagerFileTreeBuilder to run
> listFiles() first, then populate(), then add the nodes--
> in other words, recurse the tree entirely, and
> add nodes from the bottom up. Sorry, that sounds
> confusing as I write it, need to mock this up.
>

I think what you are trying to achieve is:
(1) exclude all leaf and container nodes that don't match the filter, AND
(2) exclude any container node that *might* be empty after the specified filter has been applied to all of its descendants, right down to the last leaf node. If that is true, then you can't generalize for both eager and lazy builders, as you are pretty much have to build your entire model eagerly.

Is that what you want to achieve?

Ramesh

Patrick Wright

Hey Ramesh

> I think what you are trying to achieve is:
> (1) exclude all leaf and container nodes that don't match the filter, AND
> (2) exclude any container node that *might* be empty after the specified filter has been applied to all of its descendants, right down to the last leaf node. If that is true, then you can't generalize for both eager and lazy builders, as you are pretty much have to build your entire model eagerly.

Yes, and 2) is only possible in the eager model--which is OK with me,
as long as their is a straightforward way to code it. I know there is,
just haven't been able to look at it today. I think the trick is
making the call to populate() before adding nodes, so that the entire
branch is recursed first, then returning back up and adding nodes only
if descendants were found. I think this is a pretty standard use-case
for a TreeModel.

We could have this be a third model, so we have deferred, eager and complete.

Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> > I think what you are trying to achieve is:
> > (1) exclude all leaf and container nodes that don't
> > match the filter, AND
> > (2) exclude any container node that *might* be
> > empty after the specified filter has been applied to
> > all of its descendants, right down to the last leaf
> > node. If that is true, then you can't generalize for
> > both eager and lazy builders, as you pretty much
> > have to build your entire model eagerly.
>
> Yes, and 2) is only possible in the eager model--
> which is OK with me, as long as their is a
> straightforward way to code it. I know there is,
> just haven't been able to look at it today. I think
> the trick is making the call to populate() before
> adding nodes, so that the entire branch is
> recursed first, then returning back up and
> adding nodes only if descendants were found.
> I think this is a pretty standard use-case
> for a TreeModel.
>
> We could have this be a third model, so we have
> deferred, eager and complete.
>
> Patrick

Hey Patrick,

Thanks for helping me validate the api. Seems like your use case is covered! Check out the demo at https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI... . It has three file tree builders -- eager, lazy, and pruned. The last one is just like the first, except that it prunes empty container nodes while the tree is being built (It never adds them to the tree).

From here, it should be easy to generalize the builders for other types of object structures (not just file systems) and other types of filters (not just FileFilter).

Ramesh

wrandelshofer
Offline
Joined: 2004-11-11

rameshgupta,

Please forgive me for posting to such an old thread.

I am working for some time now (but with low priority) on model extensions for JList, JTree and JTable in order to minimize boilerplate code for clipboard support and drag and drop support.

I am currently having working implementations of JTree, JTable and JList components with clipboard support. And I have started with drag and drop support for JList.

For drag and drop support, I believe, that I need to implement drag and drop gesture support in the look and feel. Currently, if a drag gesture occurs, the UI delegates in the look and feel change the selection. Which makes the components hard to use.

Now, before I start fiddling around with the UI components, I thought, I look around a bit for existing solutions. Maybe I don't have to invent the wheel again.

Apparently the model extensions I came up with are very similar to the ones you proposed.

For more information see the sample coded and JavaDoc here:
http://www.randelshofer.ch/oop/dnd/index.html

I noticed that the code you posted at

https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI...

has last been modified on August 2005.

Have you made progress in the meantime on this? Do you have working implementations of UI components as well or are the models currently all there is?

Patrick Wright

Hi Ramesh

This is the first cut of the code, and my main goal was to get my head
around the Visitor pattern, which seems applicable in this case. Most
of your concerns stem from the fact this is a first version.

Whether we use this idea at all, or whether we use another, this one
will surely be refactored. I started with a very traditional (from the
book) Visitor pattern, and went from there, hence the rather verbose
code.

> From what I understand now, you are presenting a way to populate a tree model using a data source, by applying a filter to the original data source before the tree model is populated and attached to the view. That's not what I had understood earlier. What I thought you were proposing then is to defer populating nodes in the tree model until users expanded a parent node in the tree view.

No, I was talking about both. If we have data sources for which one
can build generic traversal mechanisms, such as
a filesystem
a database or dataset
an xml file
a reflection hierarchy

These traversal mechanisms either already exist or could be written
pretty easily.

then you can use one traversal mechanism that just feeds you the nodes
in some order (depending on how you want to walk the tree). That
"traversal mechanism" is the Object Structure in the pattern, and here
it's called TreeStructure, with a traverse method.

You then might say, I want to filter out certain nodes in the tree. In
this case, I'm doing that during the traversal, but it could also
happen as the nodes are visited. But in either case the API would have
a filter mechanism similar to FileFilter (which is used in this
example).

Separately, you want to build nodes in the tree at one point or
another. Which nodes you build, what accessory or user objects you
have, are handled by the Visitor--the one who "visits" the node on the
tree.

Last, you may want to defer expansion. *That is not coded yet* because
my first goal was to see how the Visitor pattern worked and how to
apply it in this case.

So you have four degrees? of freedom:
- the data source, and the order of traversal
- which nodes in the datasource are selected
- what MutableTreeNode implementations to attach to the tree
- when to expand nodes

Of these, the current prototype has you select the data source (can't
do it for you), selects all nodes unless you pass a filter, and just
attaches a DefaultMutableTreeNode to the tree.

>
> In your example, you show a way to pluck selected files from a file system, and attach them to newly created nodes in the tree model. Thus, filtering happens during model construction, not later in a view. I think this is fine, because there are situations where one would want one's model to be a subset of the universe.

Yes, we'd have to talk about how this squares with "dynamic" filtering
constructs.

> Although I understood the concept, I had some trouble getting my head around the source code -- There are more than a dozen files! Still, from what I understand, DirectoryTNBuilder encapsulates the equivalent of the static add() method that I had in my example, while FileTreeStructure encapsulates the equivalent of the static populate() method from my example, augmented, of course, with filtering capability.

Yes, DirectoryTNBuilder is a visitor, in this case with a hard-coded
"build" operation. You could subclass it and override the
createTreeNode() method to build your own MutableTreeNode of whatever
type you like.

FileTreeStructure represent the "tree" of the filesystem, and in this
case, as in yours, uses recursion to traverse. Would have different
"tree structures" for different types of data source (reflection,
dataset, xml).

The Visitor pattern has three moving parts (object structure, element,
visitor) which then have to have implementations, which for
convenience might have abstract implementations--yes, it can and
should be simplified, but you can swap out any of the parts.

>
> The place where I got lost was in the large number of interfaces and classes, some of which didn't seem necessary (in a very cursory reading). Can you please describe the minimal set of interfaces/classes that will get the job done?

Pick a TreeStructure:
new FileTreeStructure(dir)

optionally add a filter:
new FileTreeStructure(dir, filter)

ask a the structure to send a visitor to each node:
builder = new DirectoryTNBuilder(myTreeModel);
treeStructure.traverse(builder)

roughly 3 lines of code if you accept the default cases.

> Here is a simple exercise that might help. We can cut out the button, action listener, file chooser, and other similar stuff if we stick to the data source that I use in my example, to demonstrate the filtering capability in your example. Instead of interactively choosing a directory with which to seed the tree model, we could hard code JTree.class as the accessory for the root, just as in my example. Then, for a filtering demo, you could show what would be required to populate the "Methods" group with only methods that start with "get" or "set".

I am currently writing the necessary classes to walk a class tree
using reflection--it's a bunch of refactoring and only my second use
of the API, so give me a day or two. Yes, it will be more clear to
compare to your example that way.

> What do you think?

Right on the money.

Goal: without having some generic traversal mechanism, I have to write
my own looping or traversal code each time I start a project, and I
hate that. This is to make that easier.

Thanks for the feedback
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

Ramesh

Actually, just two more words--you are right, the prototype I have is
complex and I'm beginning to think (as I work on the second data
source case) that I'm using the wrong patterns here. It would seem the
traversal problem calls for an Iterator, but the defer-until-needed
problem calls for a factory, while the what-node-to-build calls for a
builder. I'll have to talk to the union. :) Whether I use patterns or
not isn't the issue, but I had this idea that Visitor was a good one
(since it's often associated with processing things like ASTs
apparently).

So--there may be radical redesign on the way.
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

Ramesh

I am going to take a different approach with building the nodes for
the model. This would be to provide an interface to MutableTreeNode
called MutableTreeNodeFactory, which then is in charge of spitting out
new nodes on demand.

I'm hacking through this right now, and it seems like it will be
simpler than the last try--but am hitting a problem. When I expand a
child on the second level (a grandchild of root), I'm hitting a
ClassCastException

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException:
[Ljavax.swing.tree.TreeNode;
at net.guptas.draft.impl.DefaultMutableTreeNode.getPath(DefaultMutableTreeNode.java:181)
at net.guptas.draft.TreeModelEventSupport.fire(TreeModelEventSupport.java:28)
at net.guptas.draft.impl.DefaultMutableTreeModel.add(DefaultMutableTreeModel.java:63)
net.guptas.draft.impl.DefaultMutableTreeNode.getPath(DefaultMutableTreeNode.java:181)
at net.guptas.draft.factory.FileMutableTreeNodeFactory.createTreeNode(FileMutableTreeNodeFactory.java:60)

Can you take a peek at that code? Your implementation package is not
checked in. My own FileMutableTreeNodeFactory.createTreeNode() method
is

public MutableTreeNode createTreeNode(MutableTreeNode
parent, N accessory, X userData, int idx) {
DynamicMutableTreeNode newNode = new
DynamicMutableTreeNodeImpl(accessory, userData, this);
treeModel.add(newNode, parent, idx);
return newNode;
}

The DynamicMutableTreeNode and impl are just there because I can't
make changes to your MutableTreeNode interface without your impl
classes on hand--so I extended MutableTreeNode for now and
DefaultMutableTreeNode.

The code is not at all ready for review yet--ust throwing things
together--but compiles and is in the incubator under
net.guptas.draft.factory

Thanks
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Ramesh
>
> I am going to take a different approach with building
> the nodes for the model. This would be to provide an
> interface to MutableTreeNode called
> MutableTreeNodeFactory, which then is in charge of
> spitting out new nodes on demand.
>

After I posted my initial draft, I made some changes to the interfaces. Based on your feedback, I changed the names of interfaces to not clash with existing Swing names. So, MutableTreeNode in the draft package has been tentatively renamed to TreeNodeExt, and DefaultMutableTreeNode in the draft.impl package has been renamed to DefaultTreeNodeExt.

One of the more significant changes is that the model interface now has factory methods to create and add nodes.
[code]
public TreeNodeExt createNode(
N accessory, T userData, boolean allowsChildren);

public TreeNodeExt add(
N accessory, T userData, boolean allowsChildren,
TreeNodeExt parent);

public TreeNodeExt add(
N accessory, T userData, boolean allowsChildren,
TreeNodeExt parent, int index);
[/code]
Not sure yet how this will affect what you have written, but let me take a look.

> When I expand a child on the second level (a
> grandchild of root), I'm hitting a ClassCastException
>
> Can you take a peek at that code?

Sure, let me take a look at why we are getting an exception.

Ramesh

rameshgupta
Offline
Joined: 2004-06-04

> > Ramesh
> >
> > I am going to take a different approach with
> > building the nodes for the model. This would be
> > to provide an interface to MutableTreeNode called
> > MutableTreeNodeFactory, which then is in charge of
> > spitting out new nodes on demand.
> >
>
> After I posted my initial draft, I made some changes
> to the interfaces. Based on your feedback, I changed
> the names of interfaces to not clash with existing
> Swing names. So, MutableTreeNode in the draft package
> has been tentatively renamed to TreeNodeExt, and
> DefaultMutableTreeNode in the draft.impl package has
> been renamed to DefaultTreeNodeExt.
>
> One of the more significant changes is that the model
> interface now has factory methods to create and add
> nodes.
> [code]
> public TreeNodeExt createNode(
> N accessory, T userData, boolean allowsChildren);
>
> public TreeNodeExt add(
> N accessory, T userData, boolean allowsChildren,
> TreeNodeExt parent);
>
> public TreeNodeExt add(
> N accessory, T userData, boolean allowsChildren,
> TreeNodeExt parent, int index);
> [/code]
> Not sure yet how this will affect what you have
> written, but let me take a look.

Patrick,

I finally got the filter and deferred node loading code to work, though it took quite a bit of hacking ;-) In places, I didn't really understand your code, so I used my best guess, and might have accidently second-guessed you. The fixed up code is messy and unwieldy right now. I'll scrub it and post it tomorrow, I hope, but definitely by Friday.

Overall, I like your approach!

Ramesh

Patrick Wright

Hi Ramesh--Please hack away; my code wasn't finished and it's good
that the bug I hit is getting a second pair of eyes to look at this. I
look forward to seeing what you've worked out.

Regards
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Hi Ramesh--Please hack away; my code wasn't finished
> and it's good that the bug I hit is getting a second pair
> of eyes to look at this. I look forward to seeing what
> you've worked out.
>
>
> Regards
> Patrick

One of the reasons why the example got tangled up was that it was trying to show off two independent features (filtering and lazy node construction) at once. I'll try to tease them apart in separate mini demos if that is alright with you.

Ramesh

Patrick Wright

> One of the reasons why the example got tangled up was that it was trying to show off two independent features (filtering and lazy node construction) at once. I'll try to tease them apart in separate mini demos if that is alright with you.

Sounds great. I think eventually we want to be able to demonstrate the
different moving parts: deferred installation, filtering, custom node
creation, and tree model mutability.

Cheers
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Hi Ramesh
>
> > OK, I posted the interfaces, along with the source code for
> > a simple demo and an executable jar file for the demo at
> > https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI...
>
> The code is very clear, easy to follow. (Can I suggest
> checking this in to the incubator, under rameshgupta/src/java?
>
> (FWIW: Locally, I created a src/rameshgupta/java and
> ... [snip] and was able to compile and run.
> I can check this in if you like.)

Sure! Thanks.

>
> First thoughts (and I apologize if these are dumb
> questions) I'm not clear on the difference between
> the "user object" and the "accessory" and "user data".
> JDK DefaultMutableTreeNode has "user object", while
> the other two are in your MutableTreeNode
> implementation. First--naming? Second--purpose? Why
> two (or three)?
>

I had a feeling that I missed something in the hastily written documentation. There are only two things (not three).

First, naming: Ever since the W3C standardized on set/getUserData in the DOM Node interface, I have been using that in my code everywhere. The JAXP implementation in 5.0 maps set/getUserData in the Node interface to set/getUserObject in the Xerces implementation.

Since Swing's MutableTreeNode interface doesn't have a method to set user data, I just chose to add setUserData rather than setUserObject, and map it to setUserObject in the implementation. Now my code just calls setUserData regardless of whether it is dealing with a DOM Node interface or Swing TreeNode interface.

Second, purpose: In a lot of my models, I have the need to attach instances of standard Java classes to nodes in my tree, and I often have to augment that with additional info. I used to deal with this situation by wrapping instances of standard Java classes (or any third-party classes) in custom objects that carried the additional info I needed for each node, and then setting the wrapper object as the user object for the node. Over time, I got tired of this, and found that it was more convenient to have separate slots for my objects (the userData slot), and a separate slot for 3rd-party objects (the accessory slot). This step alone significantly reduced my need to sublclass nodes.

> DefaultMutableTreeNode has a large number of helper
> methods (getFirstXXX(), sibling methods, etc.) plus support
> for TreePaths. How do you decide which of those are in the
> interface and which aren't?

Through consensus ;-) Remember, we are looking at an early draft. At this point, we can easily add/remove/change methods as needs unfold.

> Maybe some of these can be pushed into a helper
> class? Annoying, since those methods aren't declared
> in TreeNode or MutableTreeNode in the JDK...
> but maybe your MutableTreeNode could
> support a new TreeNodeNavigator with a default
> implementation (which could be swapped out
> for a more efficient one for certain backing data
> structures). I actually like this idea because most
> people would never want to write their own
> Navigator class themselves, but it seems
> useful to have, even when not using the JDKs
> DefaultMutableTreeNode.

Why don't you come up with a proposal with some pseudocode? I started going down this path with helper classes for event handling in my DefaultMutableTreeModel implementation. This gives it a much smaller surface area compared to Swing's DefaultTreeModel implementation, while allowing me to reuse the helper class in other model implementations that may not descend from my DefaultMutableTreeModel implementation.

>
> Speaking of which, since this might go in a SwingLabs
> project? then maybe your implementation shouldn't be
> called DefaultMutableTreeNode as well...it seems it
> would be nice to include this in SwingLabs
> somewhere, or else people will need to wait until
> Mustang (if you have time to even make the cut).

I am not wedded to any names. Feel free to make suggestions. BTW, there is no way I can see this going into Mustang at this point in time :-) So, SwingLabs would be a natural home for it if enough people find it interesting.

>
> Nit: Why the call to parent.getChildCount() when
> adding a new node to the tree.add(newNode, parent,
> parent.getChildCount())? You are already passing the parent...

The last argument to MutableTreeModel.add() is the index at which to add. Passing the parent only is not enough.

>
> What if we extract a Strategy + Abstract Builder
> pattern for building new nodes? I'm thinking that
> for some straightforward cases, we could
> help the developer avoid their own recursive
> functions: such as a tree of files (using a FileFilter),
> a dataset tree (following relations), a tree of properties
> (splitting on '.'). Not sure if I can make it work,
> but will mock this up.
>

I have this coded up for tree paths separated by an arbitrary path separator (e.g., string file names with '/' or ':' as the separator). I didn't think there would be interest in putting such specific cases in a toolkit.

>
> All in all, I like how simple TreeModelDemo is.
>
> Regards
> Patrick

Thanks for your well-written and thorough feedback.

Ramesh

Patrick Wright

Hi Ramesh

Thanks for your detailed reply.
> > The code is very clear, easy to follow. (Can I suggest
> > checking this in to the incubator, under rameshgupta/src/java?

This is checked into the incubator
src/rameshgupta/java
lib/treedemo.jar

You need the jar in your classpath because there are a couple of
missing impl classes needed for the demo.

> > questions) I'm not clear on the difference between
> > the "user object" and the "accessory" and "user data".
>
> First, naming: Ever since the W3C standardized on set/getUserData in the DOM Node interface, I have been using that in my code everywhere. The JAXP implementation in 5.0 maps set/getUserData in the Node interface to set/getUserObject in the Xerces implementation.
>
> Since Swing's MutableTreeNode interface doesn't have a method to set user data, I just chose to add setUserData rather than setUserObject, and map it to setUserObject in the implementation. Now my code just calls setUserData regardless of whether it is dealing with a DOM Node interface or Swing TreeNode interface.

Hmm. I see. I think, though, that the weight of the existing consensus
is going to be against you here. The JDK DefaultMutableTreeNode
already uses "user object", so unless we can explain to people how the
SwingX MutableTreeNode is different, it seems like we are stuck.

>
> Second, purpose: In a lot of my models, I have the need to attach instances of standard Java classes to nodes in my tree, and I often have to augment that with additional info. I used to deal with this situation by wrapping instances of standard Java classes (or any third-party classes) in custom objects that carried the additional info I needed for each node, and then setting the wrapper object as the user object for the node. Over time, I got tired of this, and found that it was more convenient to have separate slots for my objects (the userData slot), and a separate slot for 3rd-party objects (the accessory slot). This step alone significantly reduced my need to sublclass nodes.

Ahh. So you might have a File as your user object, and an accessory
that somehow describes the file separately. I understand the goal.
Hmm. Tough one. Reminds me a little bit of "name" in AWT Component. It
is "the name of the component". What the hell does that mean? I've
heard of people using this for things like localization, but the
purpose/intent seem under-determined.

So, same with accessory, my gut feeling anyhow. The point or argument
might be that sometimes you have an object you can't or don't want to
extend that you need other information about in your tree, and so two
slots are provided for you. Any my guess is the counter-argument would
be that in Java we are very used to creating stupid little wrapper
classes just for this purpose, not that any of us like it very much.

I'd argue that point needs more discussion. In any case, it seems like
accessories necessarily would never be used directly by the
MutableTreeNode implementation, correct? In which case people can
subclass the MTN implementation and add an "set/getAccessory" to that
class if they want (1, 2, 3...n) extra data classes.

> > DefaultMutableTreeNode has a large number of helper
> > methods (getFirstXXX(), sibling methods, etc.) plus support
> > for TreePaths. How do you decide which of those are in the
> > interface and which aren't?
>
> Through consensus ;-) Remember, we are looking at an early draft. At this point, we can easily add/remove/change methods as needs unfold.

OK

> > Maybe some of these can be pushed into a helper
> > class? Annoying, since those methods aren't declared
> > in TreeNode or MutableTreeNode in the JDK...
> > but maybe your MutableTreeNode could
> > support a new TreeNodeNavigator with a default
> > implementation (which could be swapped out
> > for a more efficient one for certain backing data
> > structures). I actually like this idea because most
> > people would never want to write their own
> > Navigator class themselves, but it seems
> > useful to have, even when not using the JDKs
> > DefaultMutableTreeNode.
>
> Why don't you come up with a proposal with some pseudocode? I started going down this path with helper classes for event handling in my DefaultMutableTreeModel implementation. This gives it a much smaller surface area compared to Swing's DefaultTreeModel implementation, while allowing me to reuse the helper class in other model implementations that may not descend from my DefaultMutableTreeModel implementation.

Hmm. This is going to be a bear of a discussion. IMO the surface area
of DefaultMutableTreeNode is too large; I understand the intents, but
would rather externalize a number of the methods, as I said above.
What I'm not sure of is how to square your MutableTreeNode
implementation with the JDKs, and then how to square DMTN in the JDK
with yours. In other words, how far off can the SwingX version be and
still be accepted? That we need more input on.

As for pseudo-code from me, more below.

> > What if we extract a Strategy + Abstract Builder
> > pattern for building new nodes? I'm thinking that
> > for some straightforward cases, we could
> > help the developer avoid their own recursive
> > functions: such as a tree of files (using a FileFilter),
> > a dataset tree (following relations), a tree of properties
> > (splitting on '.'). Not sure if I can make it work,
> > but will mock this up.
> >
>
> I have this coded up for tree paths separated by an arbitrary path separator (e.g., string file names with '/' or ':' as the separator). I didn't think there would be interest in putting such specific cases in a toolkit.

I am starting to play with this using the patterns I noted elsewhere
in the thread. Basically, I hate writing recursive methods--or rather,
I think it's a lot of fun but not when I'm under pressure. So I like
the idea of having certain "traversers" for some common data
structures, and a general approach for writing new ones, that allows
one to build tree models more easily...whether I can pull that off in
an approach that people like is another question. Am currently hacking
through the undergrowth, more to follow.

Once I have that, I'll see if I have an idea for making a simple DMTN
by pulling out some of the navigation methods; but not sure how much
code this would affect elsewhere.

Thanks again, Ramesh.

Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Hi Ramesh
>
> Thanks for your detailed reply.
> > > The code is very clear, easy to follow.
>
> This is checked into the incubator

Thanks!

>
> > > I'm not clear on the difference between
> > > "user object", "accessory" and "user data".
> >
> > First, naming: Ever since the W3C standardized on
> > set/getUserData in the DOM Node interface, I have
> > been using that in my code everywhere. The JAXP
> > implementation in 5.0 maps set/getUserData in the
> > Node interface to set/getUserObject in the Xerces
> > implementation.
> >
> > Since Swing's MutableTreeNode interface doesn't
> > have a method to set user data, I just chose to add
> > setUserData rather than setUserObject, and map it to
> > setUserObject in the implementation. Now my code just
> > calls setUserData regardless of whether it is dealing
> > with a DOM Node interface or Swing TreeNode
> > interface.
>
> Hmm. I see. I think, though, that the weight of the
> existing consensus is going to be against you here.
> The JDK DefaultMutableTreeNode already uses "user object",
> so unless we can explain to people how the SwingX
> MutableTreeNode is different, it seems like we are stuck.

They are not different in their intent, only in their name. The JDK has plenty of precedents for changing method names in classes :-) That said, I am not hung up on the name. I just find it convenient to use the same method name in DOM nodes and Tree nodes.

> >
> > Second, purpose: In a lot of my models, I have the
> > need to attach instances of standard Java classes to
> > nodes in my tree, and I often have to augment that
> > with additional info. I used to deal with this
> > situation by wrapping instances of standard Java
> > classes (or any third-party classes) in custom
> > objects that carried the additional info I needed for
> > each node, and then setting the wrapper object as the
> > user object for the node. Over time, I got tired of
> > this, and found that it was more convenient to have
> > separate slots for my objects (the userData slot),
> > and a separate slot for 3rd-party objects (the
> > accessory slot). This step alone significantly
> > reduced my need to sublclass nodes.
>
> Ahh. So you might have a File as your user object,
> and an accessory that somehow describes the file
> separately. I understand the goal. Hmm. Tough one.
>
> The point or argument might be that sometimes
> you have an object you can't or don't want to
> extend that you need other information about in your
> tree, and so two slots are provided for you.

The two slots are semantically different. The userData slot is just that -- user data, and is completely ignored by the toolkit. The accessory, on the other hand, is an accomplice ;-) that helps shape or mold the node without requiring developers to subclass the node. The default node implementation uses the accessory (see below).

>
> I'd argue that point needs more discussion. In any
> case, it seems like accessories necessarily would
> never be used directly by the MutableTreeNode
> implementation, correct?
>
The default node implementation uses accessory in its toString() method:
[code]
@Override
public String toString() {
return accessory == null ? super.toString() : accessory.toString();
}
[/code]
I can also imagine firing a nodeChanged notification when the accessory is set/reset/changed, although I am not doing that right now. No such notifications are warranted whenever user data changes.

Ramesh

Patrick Wright

> The default node implementation uses accessory in its toString() method:
> [code]
> @Override
> public String toString() {
> return accessory == null ? super.toString() : accessory.toString();
> }
> [/code]
> I can also imagine firing a nodeChanged notification when the accessory is set/reset/changed, although I am not doing that right now. No such notifications are warranted whenever user data changes.

Makes more sense with this example.

Would it be possible for you to upload the default implementations as
well? I know we only code to interfaces ;) but it would be useful for
clarifying stuff like this.

If the code has licensing issues or cleanup, understood.

Thanks
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

Hi thread

I've just checked in a rough implementation of the Vistor pattern for
building TreeModels. To take advantage of Ramesh's recent
contributions, I checked this into his incubator directory,
rameshgupta.

If you synchronize with CVS, you can run by changing your build.property file
# java.net.id: Identify the developer's package and *must* be set
java.net.id = rameshgupta

run.class = net.guptas.draft.builder.file.TreeModelFileDemo

This is a variation on Ramesh's TreeModelDemo. In this case,
everything is the same, but I'm using a Visitor pattern to work
through the directories and build the model up. From the top level,
the code looks like this

FileTreeStructure d = new FileTreeStructure(dir, filter);
MutableTreeModel model = createTreeModel(dir, null);
DirectoryTNBuilder builder = new DirectoryTNBuilder(model);
d.traverse(builder);

jTree.setModel(model);

The FileTreeStructure is what traverses the "tree" starting at the
root directory. The DirectoryTNBuilder is the visitor that in this
case builds the model as the traversal takes place.

The advantage of this approach is that you can build TreeStructures
that can traverse any structure (beans, JAR files, classes, datasets)
and do what you want with them (print them out, build models, etc.).

This is just a first cut; it's the first time I've used the pattern.
There are obvious improvements (like using Generics in the right
places), plus some standard TreeStructures that could be supplied
(directory structure, dataset, reflection, xml using sax). Might be
interesting to see how we could use Dave's JGA in there as well. Think
of this as proof of concept.

Comments appreciated.
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> Hi thread
>
> I've just checked in a rough implementation of the
> Vistor pattern for building TreeModels.
>
> run.class =
> net.guptas.draft.builder.file.TreeModelFileDemo
>
> This is a variation on Ramesh's TreeModelDemo. In
> this case, everything is the same, but I'm using a
> Visitor pattern to work through the directories and
> build the model up. From the top level,
> the code looks like this
>
> FileTreeStructure d = new FileTreeStructure(dir,
> filter);
> MutableTreeModel model = createTreeModel(dir,
> null);
> DirectoryTNBuilder builder = new
> DirectoryTNBuilder(model);
> d.traverse(builder);
>
> jTree.setModel(model);
>
>
> The FileTreeStructure is what traverses the "tree"
> starting at the root directory. The DirectoryTNBuilder
> is the visitor that in this case builds the model as
> the traversal takes place.
>
> The advantage of this approach is that you can build
> TreeStructures that can traverse any structure (beans,
> JAR files, classes, datasets) and do what you want
> with them (print them out, build models, etc.).
>
> This is just a first cut; it's the first time I've
> used the pattern. There are obvious improvements
> (like using Generics in the right places), plus some
> standard TreeStructures that could be supplied
> (directory structure, dataset, reflection, xml using
> sax). Might be interesting to see how we could use
> Dave's JGA in there as well.
> Think of this as proof of concept.
>
> Comments appreciated.
> Patrick

Hi Patrick,

From what I understand now, you are presenting a way to populate a tree model using a data source, by applying a filter to the original data source before the tree model is populated and attached to the view. That's not what I had understood earlier. What I thought you were proposing then is to defer populating nodes in the tree model until users expanded a parent node in the tree view.

In your example, you show a way to pluck selected files from a file system, and attach them to newly created nodes in the tree model. Thus, filtering happens during model construction, not later in a view. I think this is fine, because there are situations where one would want one's model to be a subset of the universe.

Although I understood the concept, I had some trouble getting my head around the source code -- There are more than a dozen files! Still, from what I understand, DirectoryTNBuilder encapsulates the equivalent of the static add() method that I had in my example, while FileTreeStructure encapsulates the equivalent of the static populate() method from my example, augmented, of course, with filtering capability.

The place where I got lost was in the large number of interfaces and classes, some of which didn't seem necessary (in a very cursory reading). Can you please describe the minimal set of interfaces/classes that will get the job done?

Here is a simple exercise that might help. We can cut out the button, action listener, file chooser, and other similar stuff if we stick to the data source that I use in my example, to demonstrate the filtering capability in your example. Instead of interactively choosing a directory with which to seed the tree model, we could hard code JTree.class as the accessory for the root, just as in my example. Then, for a filtering demo, you could show what would be required to populate the "Methods" group with only methods that start with "get" or "set".

What do you think?

Ramesh

rbair
Offline
Joined: 2003-07-08

Interjecting...

> From what I understand now, you are presenting a way
> to populate a tree model using a data source, by
> applying a filter to the original data source before
> the tree model is populated and attached to the view.
> That's not what I had understood earlier. What I
> thought you were proposing then is to defer
> populating nodes in the tree model until users
> expanded a parent node in the tree view.

I'm curious to see how this shakes out. Just my .02, there definitely must be support for dynamically building the tree model when nodes are expanded in the view. If this mechanism doesn't support it, some mechanism must (somewhere down the road).

Richard

evickroy
Offline
Joined: 2004-07-23

> I'm curious to see how this shakes out. Just my .02,
> there definitely must be support for dynamically
> building the tree model when nodes are expanded in
> the view. If this mechanism doesn't support it, some
> mechanism must (somewhere down the road).

+1

The Oracle ADF framework currently does this with a custom NodeBinding that builds the nodes from a data view. The child nodes aren't fetched until the node is expanded.

Erik

allquixotic
Offline
Joined: 2005-05-31

I agree that subclassing the model is a little verbose for the average developer. It provides great flexibility for all sorts of uses of TreeTables, but when you think about it, Swing (and perhaps Smalltalk) is/are the only APIs that go this path. Microsoft's ListView control; SWT's TableTree; countless proprietary .NET and COM add-in controls; and even simpler Swing controls (like Labels and JTextFields) have a direct object model for complete manipulation of the object (including a pre-selected storage mechanism) in a purely object-[i]based[/i] format. It wouldn't be hard for SwingX to standardize on a single "full" implementation of a TreeTable and provide this as a default to the developer, would it? I mean if you're thinking about going halfway and providing a mutable interface, why not go all the way and provide an [i]exemplar implementation[/i] that has a fully-implemented standard feature set? For example:

public class Smidget{
protected JNETreeTable jnett;
public Smidget(String[] ... nodes){
jnett = new JNETreeTable();//E for 'exemplar'
someFrame.add(jnett);
for(String[] sa : nodes)
{
try{
jnett[b].addBaseNode[/b](sa);

}
catch([b]TooManyColumnsException[/b] tmce)
{
System.out.println("Error adding " + sa.toString());
tmce.printStackTrace();
}
}
int[] nodePath = new int[]{0,1};
jnett[b].addNode(nodePath,new String[]{"Hello","John","1234"})[/b];
jnett[b].setValue(nodePath,2,"5678")[/b];
jnett.removeBaseNode(8);
nodePath = //something else;
jnett[b].removeNode(nodePath)[/b];

nodePath = jnett[b].findValue("John")[/b];
...
}

private void fireSomethingHappened()
{
String[] values = jnett.getValues(some_nodepath);
jnett[b].clear()[/b];
...
}
}
This could be translated to an object-oriented design, to better fit in with existing and future tables. Plus the cell renderers could still remain hidden behind this simple data-based view of the JNETreeTable; the default cell renderer would be the JLabel of course! It may look like I'm cutting a lot of corners here, but VB developers need at least something tangible (that "does stuff" in 3 lines of code) or it just appears that JXTreeTable/JNTreeTable is a useless skeleton interface that doesn't actually do anything but lay out the structure of a proper implementation.

rbair
Offline
Joined: 2003-07-08

> What do people think of an extended tree model
> interface (e.g., [b]MutableTreeModel[/b]) that fixes
> these problems? If done right, it should make it
> possible for people to code only to the interface,
> not to the actual implementation.

[i]My Incoherent Ramblings[/i]
I've often wondered why we don't have a MutableTableModel, MutableTreeModel, or MutableListModel. As Scott points out, mutability is added to the abstract classes rather than the interfaces, but I don't know why this has to be. As a matter of principle I prefer coding to the interfaces rather than to implementations, though as a matter of practicality I've never known it to make much difference.

Perhaps the original intent was that the models would be as simple as possible, and since JList, JTree and JTable don't provide gestures for adding & removing items anyway, why should the models? However, my opinion is that these components ought to provide such gestures/actions, allowing users to add/remove items (like the .NET Grid control). If these components contain such actions/gestures, then the models ought to support mutability.

Richard

evickroy
Offline
Joined: 2004-07-23

> Perhaps the original intent was that the models would
> be as simple as possible, and since JList, JTree and
> JTable don't provide gestures for adding & removing
> items anyway, why should the models? However, my
> opinion is that these components ought to provide
> such gestures/actions, allowing users to add/remove
> items (like the .NET Grid control). If these
> components contain such actions/gestures, then the
> models ought to support mutability.

From a simple developers viewpoint, I should only be required to interface with the component (JList, JTable, JTree, etc.) It should delegate to the model if necessary. If I, as the developer, need something more complicated, then I can learn how to customize the model and "plug it in" using setModel, etc. This would be the ideal approach for ease of use in my opinion.

I find it difficult to explain the reasoning behind separation of component and model to new Java developers.

If anyone cares. ;)
Erik

Scott Violet

Hi Ramesh,

jdnc-interest@javadesktop.org wrote:
> The existing Swing [b]TreeModel[/b] interface is incomplete, lacking
> important methods, such as those for inserting and removing nodes.

For the most part ListModel, TableModel and TreeModel are immutable.
The Abstract* implementations provide the mutability. Tree doesn't have
an abstract TreeModel in core, but there are variants floating around
out there. You could add one to core.

> As a
> result, developers either have to extend the interface and take the
> responsibility of model management upon themselves, or they have to
> forget the interface, and limit themselves to [b]DefaultTreeModel[/b].
> Even in the latter case, the onus for calling DefaultTreeModel methods,
> such as [b]nodesWereInserted[/b] and [b]nodesWererRemoved[/b], lies on
> developers, unless they code directly to the DefaultTreeModel API
> (calling [b]insertNodeInto[/b] or [b]removeNodeFromParent[/b]).
> Unfortunately, by doing so, they foreclose their ability to use a custom
> tree model class that does not extend DefaultTreeModel.

I would investigate either an AbstractTreeModel or perhaps an
alternative implementation that doesn't use TreeNodes.

> What do people think of an extended tree model interface (e.g.,
> [b]MutableTreeModel[/b]) that fixes these problems? If done right, it
> should make it possible for people to code only to the interface, not to
> the actual implementation.

How would a new interface make it easy for developers to write mutable
implementations?

> The interface would be backward compatible in that any code that expects
> a TreeModel would accept MutableTreeModel. Does it make sense to pursue
> model extensions like this?

From JTree (and JTreeTable) perspective they don't need the mutability.
The mutability is typically only useful for applications developers
and so I'm not sure that a mutable treemodel is gaining much...

-Scott

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> > The existing Swing [b]TreeModel[/b] interface is
> > incomplete, lacking important methods, such as
> > those for inserting and removing nodes.
>
> For the most part ListModel, TableModel and TreeModel
> are immutable.
> The Abstract* implementations provide the mutability.
>

Scott,

Yeah, you're right. But, there is precedent in MutableComboBoxModel ;-)

Swing supports mutability in the MutableTreeNode interface, just not in the TreeModel interface. What makes this awkward is that I can mutate the model without going through the model api at all. After I am done, Swing expects me to call the model to let it know that I messed with something in there. However, my expectation is that if I use a mutation method in a toolkit API, the toolkit should automatically fire event notifications.

>
> How would a new interface make it easy for developers
> to write mutable implementations?
>
> From JTree (and JTreeTable) perspective they don't
> need the mutability. The mutability is typically only useful
> for applications developers and so I'm not sure that a
> mutable treemodel is gaining much...
>
> -Scott

From a toolkit developer's perspective, what you are saying is correct. But from an app developer's perspective I don't want to code directly to the model implementation class because a lot of my code uses common patterns without regard to the actual model class.

The root of my problem is that this common code also deals with model mutability. I got around this by defining my own model interface extensions, and sticking to those rather than the concrete implementation classes.

The extended interfaces give my applications some immediate benefits:
(1) Type safety -- Thanks to covariant return types, the extended interface can redeclare methods such as getRoot() to return more specific types.
(Compare getRoot and setRoot in DefaultTreeModel, in contrast.)

(2) Minimal subclassing -- Exploiting Generics and user-data support in nodes, it is possible to define model implementation classes that drastically reduce the need for subclassing the model and node classes.

I guess all of this can be done directly in implementation classes, but my goal is to avoid having to write to an implementation class.

Ramesh

Scott Violet

Hi Ramesh,

jdnc-interest@javadesktop.org wrote:
>>>The existing Swing [b]TreeModel[/b] interface is
>>>incomplete, lacking important methods, such as
>>>those for inserting and removing nodes.
>>
>>For the most part ListModel, TableModel and TreeModel
>>are immutable.
>>The Abstract* implementations provide the mutability.
>>
>
>
> Scott,
>
> Yeah, you're right. But, there is precedent in MutableComboBoxModel ;-)

Yes. This is necessary because combobox, out of the box, needs
mutability. That isn't really the case with the other modesl.

> Swing supports mutability in the MutableTreeNode interface, just not in
> the TreeModel interface. What makes this awkward is that I can mutate
> the model without going through the model api at all. After I am done,
> Swing expects me to call the model to let it know that I messed with
> something in there. However, my expectation is that if I use a mutation
> method in a toolkit API, the toolkit should automatically fire event
> notifications.

Yes, this is unfortunate. DefaultMutableTreeNode was designed to be
useful by itself. That is, it could be used independant of JTree.
Ideally DMTN would have been in the java.util package, in which case it
would have been more understandable that mutations directly done to DMTN
don't go through the model.

>>How would a new interface make it easy for developers
>>to write mutable implementations?
>>
>>From JTree (and JTreeTable) perspective they don't
>>need the mutability. The mutability is typically only useful
>>for applications developers and so I'm not sure that a
>>mutable treemodel is gaining much...
>>
>> -Scott
>
>
> From a toolkit developer's perspective, what you are saying is correct.
> But from an app developer's perspective I don't want to code directly to
> the model implementation class because a lot of my code uses common
> patterns without regard to the actual model class.

That's if you can find a set of common mutation methods that are useful.

> The root of my problem is that this common code also deals with model
> mutability. I got around this by defining my own model interface
> extensions, and sticking to those rather than the concrete
> implementation classes.

It would be good to post those for all to comment on.

> The extended interfaces give my applications some immediate benefits:
> (1) Type safety -- Thanks to covariant return types, the extended
> interface can redeclare methods such as getRoot() to return more
> specific types.
> (Compare getRoot and setRoot in DefaultTreeModel, in contrast.)

Generifying Swing missed 1.6:( Hopefully we can do it for 1.7.

> (2) Minimal subclassing -- Exploiting Generics and user-data support in
> nodes, it is possible to define model implementation classes that
> drastically reduce the need for subclassing the model and node classes.
>
> I guess all of this can be done directly in implementation classes, but
> my goal is to avoid having to write to an implementation class.

I've no doubt there could be an improved DefaultTreeModel and the like.
If you could post it that would be great!

-Scott

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> I've no doubt there could be an improved
> DefaultTreeModel and the like.
> If you could post it that would be great!
>
> -Scott
>

Patrick, Scott,

Let me lift the code from my application, and sanitize it a bit before I post it for you. Will do that later today or tomorrow.

Ramesh

rameshgupta
Offline
Joined: 2004-06-04

> > I've no doubt there could be an improved
> > DefaultTreeModel and the like.
> > If you could post it that would be great!
> >
> > -Scott
> >
>
> Patrick, Scott,
>
> Let me lift the code from my application, and
> sanitize it a bit before I post it for you. Will do
> that later today or tomorrow.
>
> Ramesh

OK, I posted the interfaces, along with the source code for a simple demo and an executable jar file for the demo at https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI...

Please let me know what you think.

Ramesh

Patrick Wright

Hi Ramesh

> OK, I posted the interfaces, along with the source code for a simple demo and an executable jar file for the demo at https://jdnc-incubator.dev.java.net/servlets/ProjectDocumentList?folderI...

The code is very clear, easy to follow. (Can I suggest checking this
in to the incubator, under rameshgupta/src/java? For some reason
appeared not to be picked up in a CVS sync, had to download it).

(FWIW: Locally, I created a src/rameshgupta/java and put the Java
files under there, then added the JAR file to lib/rameshgupta to pick
up the implementation classes. For NetBeans, the src/rameshgupta/java
was added as a source path and the JAR file to the classpath. I
changed my build.properties to
java.net.id = rameshgupta
run.class = net.guptas.draft.demo.TreeModelDemo
and was able to compile and run. I can check this in if you like.)

First thoughts (and I apologize if these are dumb questions)
I'm not clear on the difference between the "user object" and the
"accessory" and "user data". JDK DefaultMutableTreeNode has "user
object", while the other two are in your MutableTreeNode
implementation. First--naming? Second--purpose? Why two (or three)?

DefaultMutableTreeNode has a large number of helper methods
(getFirstXXX(), sibling methods, etc.) plus support for TreePaths. How
do you decide which of those are in the interface and which aren't?
Maybe some of these can be pushed into a helper class? Annoying, since
those methods aren't declared in TreeNode or MutableTreeNode in the
JDK...but maybe your MutableTreeNode could support a new
TreeNodeNavigator with a default implementation (which could be
swapped out for a more efficient one for certain backing data
structures). I actually like this idea because most people would never
want to write their own Navigator class themselves, but it seems
useful to have, even when not using the JDKs DefaultMutableTreeNode.

Speaking of which, since this might go in a SwingLabs project? then
maybe your implementation shouldn't be called DefaultMutableTreeNode
as well...it seems it would be nice to include this in SwingLabs
somewhere, or else people will need to wait until Mustang (if you have
time to even make the cut).

Nit: Why the call to parent.getChildCount() when adding a new node to
the tree.add(newNode, parent, parent.getChildCount())? You are already
passing the parent...

What if we extract a Strategy + Abstract Builder pattern for building
new nodes? I'm thinking that for some straightforward cases, we could
help the developer avoid their own recursive functions: such as a tree
of files (using a FileFilter), a dataset tree (following relations), a
tree of properties (splitting on '.'). Not sure if I can make it work,
but will mock this up.

All in all, I like how simple TreeModelDemo is.

Regards
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

Following up on my last post

> What if we extract a Strategy + Abstract Builder pattern for building
> new nodes? I'm thinking that for some straightforward cases, we could
> help the developer avoid their own recursive functions: such as a tree
> of files (using a FileFilter), a dataset tree (following relations), a
> tree of properties (splitting on '.'). Not sure if I can make it work,
> but will mock this up.

Need to get my head around this. Take the example of building a
TreeModel for a directory, accepting only Java files. To abstract
this, I think we need three patterns working together:

Visitor: traverses the directory tree

Strategy: accepts or rejects individual files and directories (e.g.
implementation of the Visitor's Element is a Strategy that is given to
the Visitor)

Builder: for files and directories that are accepted, builds a
MutableTreeNode (or maybe a DefaultMutableTreeNode)

If we can abstract this properly, we could also have a DataSetVisitor,
a JavaBean visitor, a ReflectionVisitor, etc. Both the Strategy for
accepting the visited elements, as well as the Builder, could be
provided as arguments.

If the Visitor is provided as an argument to the MutableTreeNode, then
tree construction could be deferred until node expansion, in the sense
that instead of asking the node if it has children, you first ask the
visitor if it has expanded the node, if not the visitor expands them,
then returns the children.

Somehow we could also plug in XPath as well, for example, for
accessing a specific path on the tree (instead of using a TreePath).

Need to think more about how to shake this all out.

Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rameshgupta
Offline
Joined: 2004-06-04

> If the Visitor is provided as an argument to the
> MutableTreeNode, then tree construction could
> be deferred until node expansion, in the sense
> that instead of asking the node if it has children,
> you first ask the visitor if it has expanded the
> node, if not the visitor expands them, then
> returns the children.

Node expansion is a "view" feature. Whenever I think of MVC, I think of multiple views attached to the same model, just to make sure that we have the general case covered. Seems like what you are suggesting does not belong in a model?

>
> Somehow we could also plug in XPath as well, for
> example, for accessing a specific path on the tree
> (instead of using a TreePath).
>
> Need to think more about how to shake this all out.
>
> Patrick
>

Yeah, I have been working on building trees from paths encoded as strings. Will see what I end up with ;-)

Ramesh

Patrick Wright

On 8/25/05, jdnc-interest@javadesktop.org wrote:
> > If the Visitor is provided as an argument to the
> > MutableTreeNode, then tree construction could
> > be deferred until node expansion, in the sense
> > that instead of asking the node if it has children,
> > you first ask the visitor if it has expanded the
> > node, if not the visitor expands them, then
> > returns the children.
>
> Node expansion is a "view" feature. Whenever I think of MVC, I think of multiple views attached to the same model, just to make sure that we have the general case covered. Seems like what you are suggesting does not belong in a model?

"Expansion" is a model concept, but the question I have is whether
nodes are built when the tree is built, or deferred until needed. If
you are showing a directory tree, do you parse the whole tree first,
before showing the tree? That's what I meant. The issue is that if you
wait until the model calls hasChildren() or getChildren(), then the
node needs enough information to be able to build its child list on
expansion. If it represents a directory, say, then it needs to know
the File the directory represents, plus maybe a filter. Hence I was
saying a Visitor might be able to generalize that--hopefully the same
Visitor that started the tree, but with a different File acting as the
new "root" for expansion.

Whether this matters depends on the memory usage and cost of
performing the parsing.

Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Patrick Wright

Ramesh

> What do people think of an extended tree model interface (e.g., [b]MutableTreeModel[/b]) that fixes these problems? If done right, it should make it possible for people to code only to the interface, not to the actual implementation.

Since I, for one, find coding Tree nodes a hassle, I think this is an
interesting idea.

Could you post some pseudo-code as to what the interface for
MutableTreeModel would look like?

Thanks
Patrick

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net