tags:

views:

37

answers:

3

Hello everyone! I have a custom DefaultMutableTreeNode class that is designed to support robust connections between many types of data attributes (for me those attributes could be strings, user-defined tags, or timestamps).

As I aggregate data, I'd like to give the user a live preview of the stored data we've seen so far. For efficiency reasons, I'd like to only keep one copy of a particular attribute, that may have many connections to other attributes.

Example: The user-defined tag "LOL" occurs at five different times (represented by TimeStamps). So my JTree (the class that is displaying this information) will have five parent nodes (one for each time that tag occured). Those parent nodes should ALL SHARE ONE INSTANCE of the DefaultMutableTreeNode defined for the "LOL" tag.

Unfortunately, using the add(MutableTreeNode newChild) REMOVES newChild from WHATEVER the current parent node is. That's really too bad, since I want ALL of the parent nodes to have THE SAME child node.

Here is a picture of DOING IT WRONG (Curtis is the author and he should appear FOR ALL THE SHOWS):

alt text

How can I accomplish this easily in Java?

Update

I've been looking at the code for DefaultMutableTreeNode.add()... and I'm surprised it works the way it does (comments are mine):

public void add(MutableTreeNode child)
{
    if (! allowsChildren)
        throw new IllegalStateException();

    if (child == null)
        throw new IllegalArgumentException();

    if (isNodeAncestor(child))
        throw new IllegalArgumentException("Cannot add ancestor node.");

    // one of these two lines contains the magic that prevents a single "pointer" from being
    // a child within MANY DefaultMutableTreeNode Vector<MutableTreeNode> children arrays...
    children.add(child); // just adds a pointer to the child to the Vector array?
    child.setParent(this); // this just sets the parent for this particular instance
}
A: 

Unfortunately, I believe the answer is no. In order to do what you're talking about, you would need to have DefaultMutableTreeNode's internal userObject be a pointer to some String, so that all the corresponding DefaultMutableTreeNode's could point to and share the same String object.

However, you can't call DefaultMutableTreeNode.setUserObject() with any such String pointer, because Java does not have such a concept on the level that C or C++ does. Check out this outstanding blog article on the confusing misconceptions about pass-by-value and pass-by-reference in Java.

Update: Responding to your comment here in the answer space, so I can include a code example. Yes, it's true that Java works with pointers internally... and sometimes you have to clone an object reference to avoid unwanted changes to the original. However, to make a long story short (read the blog article above), this isn't one of those occasions.

public static void main(String[] args) {

    // This HashMap is a simplification of your hypothetical collection of values,
    // shared by all DefaultMutableTreeNode's
    HashMap<String, String> masterObjectCollection = new HashMap<String, String>();
    masterObjectCollection.put("testString", "The original string");

    // Here's a simplification of some other method elsewhere making changes to 
    // an object in the master collection
    modifyString(masterObjectCollection.get("testString"));

    // You're still going to see the original String printed.  When you called 
    // that method, a reference to you object was passed by value... the ultimate
    // result being that the original object in you master collection does 
    // not get changed based on what happens in that other method.
    System.out.println(masterObjectCollection.get("testString"));
}

private static void modifyString(String theString) {
    theString += "... with its value modified";
}

You might want to check out the JIDE Swing extensions, of which some components are commercial while others are open source and free as in beer. You might find some kind of component that comes closer to accomplishing exactly what you want.

Steve Perkins
Java has pointers, it's just very nice about them. If you don't clone data, eventually you'll notice odd things in your program because the whole time you've actually been sharing a single instance. In this case I'm adding the same DefaultMutableTreeNode as a child to multiple DefaultMutableTreeNodes. Unfortunately, the data DOES need to be cloned, or mirrored, or something, because the behavior is not at all what I expected. Isn't there some kind of Graph class in the Java specification?
Tommy Fisk
A: 

I'm not sure it qualifies as easy, but you might look at Creating a Data Model by implementing TreeModel, which "does not require that nodes be represented by DefaultMutableTreeNode objects, or even that nodes implement the TreeNode interface." In addition to the tutorial example, there's a file system example here.

trashgod
+1  A: 

If you want easily, you should probably give up on sharing the actual TreeNodes themselves. The whole model is built on the assumption that each node has only one parent. I'd focus instead on designing your custom TreeNode so that multiple nodes can all read their data from the same place, thereby keeping them synced.

bemace
I wrote a tree expansion listener that pretty much does this.
Tommy Fisk