-
FEATURED COMPONENTS
First time here? Check out the FAQ!
Hi everyone,
I got a very strange bug going on here in a tree component, which should look like this:
- [email protected] INBOX Stuff - [email protected] - foo ddd jsfh
This tree displays two email accounts. Each account gets created in a separate thread. In each thread I create TreeNode instances and add them to the parent node one by one.
Now, the strange thing is, when I run the application, I get this: http://dl.dropbox.com/u/17844821/zeug/zk_tree_duplicate.jpg
As you can see, the last account is getting duplicated. When I open that node, I get a NullPointerException and the child nodes ("ddd" and "jsfh") are not there.
So, I started to debug. My first thought was: Is the tree messing up something or is the model I created corrupt?
To test this, I added a custom tree item renderer which prints the whole TreeModel hierarchy to the console whenever the renderer for a tree item gets called.
This gave me the following:
20:11:53,120 INFO MailAccountTree:51 - [email protected] 20:11:53,120 INFO MailAccountTree:51 - INBOX 20:11:53,121 INFO MailAccountTree:51 - Stuff 20:11:53,121 INFO MailAccountTree:51 - [email protected] 20:11:53,121 INFO MailAccountTree:51 - INBOX 20:11:53,121 INFO MailAccountTree:51 - foo 20:11:53,121 INFO MailAccountTree:51 - ddd 20:11:53,121 INFO MailAccountTree:51 - jsfh
As you can see, the model does NOT contain the duplicate. So what's going on here?
Hi, Valmar!
Could you isolate your source code and post here, so we can see what's happening???
Sure, here it goes:
This creates the model for the tree:
for (MailAccount mailAccount : mailAccounts){ TreeNode node = toNode(mailAccount, true); TreeModel model = tree.getModel(); TreeNode root = (TreeNode) model.getRoot(); addNode(root, node); }
Helper functions:
private void addNode(final TreeNode parent, final TreeNode node) { Executions.schedule(desktop, new EventListener() { @Override public void onEvent(Event event) throws Exception { parent.add(node); } }, null); } /** * Converts the given mail account into a tree node * * @param account * The mail account to convert * @return The tree node * @throws MessagingException * Thrown if something went wrong while checking the mail * account's content (e.g. folders) */ public static TreeNode toNode(MailAccount account, boolean recursive) throws MessagingException { SmampiTreeNode rootNode = null; if (account != null) { IncomingMailServer incomingServer = account.getIncomingServer(); if (incomingServer != null) { Set<CachedFolder> cachedFolders = incomingServer.getCachedFolders(); List<TreeNode> children = new Vector<TreeNode>(); /* * Create a node for each folder in the mail account * (recursively with all subfolders) */ if (recursive) { if (cachedFolders != null) { List<CachedFolder> tmpCachedFolders = new ArrayList<CachedFolder>(cachedFolders); for (CachedFolder tmpCachedFolder : tmpCachedFolders) { TreeNode folderNode = toNode(tmpCachedFolder, true); if (tmpCachedFolder.isInbox(true)) { // Put INBOX to the top children.add(0, folderNode); // Set this folder as the inbox tmpCachedFolder.setFolderType(FolderType.INBOX, true); } else { children.add(folderNode); } } } } if (children != null) { if (children.size() <= 0) { children = null; } } // Create the node for "account" and its children and return it rootNode = new SmampiTreeNode(account, children); } } return rootNode; } /** * Converts the given folder into a tree node * * @param cachedFolder * The folder to convert * @return The tree node * @throws MessagingException * Thrown if something went wrong during conversion. */ public static TreeNode toNode(CachedFolder cachedFolder, boolean recursive) throws MessagingException { Set<CachedFolder> cachedFolders = cachedFolder.getCachedFolders(true); List<TreeNode> children = new Vector<TreeNode>(); // Recursively transform each (sub-)folder into a tree node if (recursive) { if (cachedFolders != null) { List<CachedFolder> tmpCachedFolders = new ArrayList<CachedFolder>(cachedFolders); for (CachedFolder tmpCachedFolder : tmpCachedFolders) { TreeNode node = null; if (tmpCachedFolder.holdsFolders(true)) { node = toNode(tmpCachedFolder, true); children.add(node); } else { node = toNode(tmpCachedFolder, true); children.add(node); } } } } if (children != null) { if (children.size() <= 0) { children = null; } } // Create the node for cachedFolder and return it TreeNode rootNode = new SmampiTreeNode(cachedFolder, children); return rootNode; }
The custom TreeNode:
/** * Own implementation of the ZK TreeNode. Will correctly return true or false if * isLeaf() is called. */ public class SmampiTreeNode extends DefaultTreeNode { private static final long serialVersionUID = 5343670451152165601L; private boolean _leaf; /** * Constructor method * * @param data * The data * @param children * The children as collection */ public SmampiTreeNode(Object data, Collection<TreeNode> children) { super(data, children); if (getChildCount() == 0) { _leaf = true; } } /** * Constructor method * * @param data * The data * @param children * The children as array */ public SmampiTreeNode(Object data, SmampiTreeNode[] children) { super(data, children); if (getChildCount() == 0) { _leaf = true; } } @Override public void insert(TreeNode child, int index) { if (child != null) { _leaf = false; super.insert(child, index); if (getChildCount() == 0) { _leaf = true; } } } @Override public boolean isLeaf() { return _leaf && getChildCount() == 0; } }
Soooo,
does anyone have an idea? :-)
Sorry , I am not actually get the process well , since what I see is how you add the node to the treemodel root ,
but it's still hard to trace what's the real process when you run it from your sample code.
Would you mind to test with tree.invalidate or try to set the model again after you add the node to root?
A quick guess is that there's a data sync issue in the treemodel,
but I am not sure which part is going wrong since I don't have a reproducible case.
Tony, you're a genius.
calling
tree.setModel(tree.getModel());
after adding all nodes did the trick!
(Smells like a ZK bug though)
Yes , if it works after you call setModel , it's more like a bug for ZK.
Let me try to reproduce it , and will inform you if I find anything. :P
Sure,
one thing though: I am processing each account in a seperate thread, so the first lines are more like:
for (MailAccount mailAccount : mailAccounts){ Thread t = new Thread(){ @Overide public void run(){ TreeNode node = toNode(mailAccount, true); TreeModel model = tree.getModel(); TreeNode root = (TreeNode) model.getRoot(); addNode(root, node); } } t.start(); }
May that's causing the trouble.
Asked: 2011-08-03 13:25:02 +0800
Seen: 461 times
Last updated: Aug 05 '11