ZK

Unexpected callbacks of AbstractTreeModel

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Fixed
  • Affects Version/s: 6.0.0
  • Fix Version/s: 6.5.2
  • Component/s: Components
  • Labels:
    None

Description

The callbacks isLeaf(), getChildCount(), getChild() are called after opening some nodes for non opened TreeItems.

Example (s attachment):
1. Open 'Sophis e' --> callbacks are ok
2. Open Bond (child of 'Sophis e') --> callbacks for parent 'Sophis e' (maybe an error), callbacks for parent 'Bond' (ok), callbacks for parent 'Calypso' and its subtrees (is an error), callbacks for all other parents (is an error)

The problem gets attention when the tree renders large data amounts.

  1. Logs.txt
    22/Aug/12 5:10 PM
    232 kB
    hshdev
  2. SimpleTreeTestModel.java
    22/Aug/12 5:10 PM
    1 kB
    hshdev
  1. Tree-Example.PNG
    8 kB
    26/Jul/12 9:07 AM

Issue Links

Activity

Hide
jimmyshiau added a comment - 22/Aug/12 8:12 AM

Hi hshdev,

To investigate this issue, I have created a model to log the timing where the APIs are invoked. I found that the API will only be called correctly, it will not invoke other parents. That is, I did not see the issue you reported.

How did you verify that the APIs are called for all other parents? Can you provide me your code for proving this? I can take another look based on your code.

BTW I have here included the sample that I used for logging:

Unable to find source-code formatter for language: java. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
package model.tree;

import org.zkoss.zul.AbstractTreeModel;
import org.zkoss.zul.TreeNode;

import test.comp.TreeTestComposer.FileInfo;

public class MyTreeModel<E> extends AbstractTreeModel<TreeNode<E>> {

	public MyTreeModel(TreeNode root) {
		super(root);
	}

	@Override
	public boolean isLeaf(TreeNode<E> node) {
		log(node, "isLeaf: ");
		return node.isLeaf();
	}

	

	@Override
	public TreeNode<E> getChild(TreeNode<E> parent, int index) {
		log(parent, "getChild: ");
		return parent.getChildAt(index);
	}

	@Override
	public int getChildCount(TreeNode<E> parent) {
		log(parent, "getChildCount: ");
		return parent.getChildCount();
	}
	
	private void log(TreeNode<E> node, String prefix) {
		if (node.getData() != null) {
			FileInfo f =  (FileInfo) node.getData();
			System.out.println(prefix + f.getPath());
			
		} else System.out.println(prefix + "null");
		
	}
	
}
Show
jimmyshiau added a comment - 22/Aug/12 8:12 AM Hi hshdev, To investigate this issue, I have created a model to log the timing where the APIs are invoked. I found that the API will only be called correctly, it will not invoke other parents. That is, I did not see the issue you reported. How did you verify that the APIs are called for all other parents? Can you provide me your code for proving this? I can take another look based on your code. BTW I have here included the sample that I used for logging:
Unable to find source-code formatter for language: java. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
package model.tree;

import org.zkoss.zul.AbstractTreeModel;
import org.zkoss.zul.TreeNode;

import test.comp.TreeTestComposer.FileInfo;

public class MyTreeModel<E> extends AbstractTreeModel<TreeNode<E>> {

	public MyTreeModel(TreeNode root) {
		super(root);
	}

	@Override
	public boolean isLeaf(TreeNode<E> node) {
		log(node, "isLeaf: ");
		return node.isLeaf();
	}

	

	@Override
	public TreeNode<E> getChild(TreeNode<E> parent, int index) {
		log(parent, "getChild: ");
		return parent.getChildAt(index);
	}

	@Override
	public int getChildCount(TreeNode<E> parent) {
		log(parent, "getChildCount: ");
		return parent.getChildCount();
	}
	
	private void log(TreeNode<E> node, String prefix) {
		if (node.getData() != null) {
			FileInfo f =  (FileInfo) node.getData();
			System.out.println(prefix + f.getPath());
			
		} else System.out.println(prefix + "null");
		
	}
	
}
Hide
hshdev added a comment - 22/Aug/12 5:10 PM

Please use class SimpleTreeModel:
Tree aggregationTree = (Tree) self.getFellow("aggregationTree");
aggregationTree.setModel(new SimpleTreeTestModel());

The tree definition in zul-file looks like this.
<tree id="aggregationTree" zclass="z-dottree" autopaging="true" mold="paging"
height="100%" width="100%" multiple="false" vflex="true"
hflex="true" style="border:none" renderdefer="0">
<treecols sizable="true">
<treecol label="Column Lable"/>
</treecols>
</tree>

The result is depicted in file 'Logs.txt'.
Cheers,

Show
hshdev added a comment - 22/Aug/12 5:10 PM Please use class SimpleTreeModel: Tree aggregationTree = (Tree) self.getFellow("aggregationTree"); aggregationTree.setModel(new SimpleTreeTestModel()); The tree definition in zul-file looks like this. <tree id="aggregationTree" zclass="z-dottree" autopaging="true" mold="paging" height="100%" width="100%" multiple="false" vflex="true" hflex="true" style="border:none" renderdefer="0"> <treecols sizable="true"> <treecol label="Column Lable"/> </treecols> </tree> The result is depicted in file 'Logs.txt'. Cheers,
Hide
jimmyshiau added a comment - 23/Aug/12 9:26 AM

Hi hshdev,

SimpleTreeModel was removed since ZK 6.0.0, what ZK version are you using?
http://books.zkoss.org/wiki/Small_Talks/2011/November/ZK_6:_Upgrade_Notes#SimpleTreeModel_and_SimpleTreeNode_are_removed

If you can tell me how you verified this issue it will be more helpful.

Show
jimmyshiau added a comment - 23/Aug/12 9:26 AM Hi hshdev, SimpleTreeModel was removed since ZK 6.0.0, what ZK version are you using? http://books.zkoss.org/wiki/Small_Talks/2011/November/ZK_6:_Upgrade_Notes#SimpleTreeModel_and_SimpleTreeNode_are_removed If you can tell me how you verified this issue it will be more helpful.
Hide
hshdev added a comment - 27/Aug/12 2:42 PM

We have not used the 'SimpleTreeModel'. Please look into file 'SimpleTreeTestModel.java'. There we use the 'AbstractTreeModel'.

Show
hshdev added a comment - 27/Aug/12 2:42 PM We have not used the 'SimpleTreeModel'. Please look into file 'SimpleTreeTestModel.java'. There we use the 'AbstractTreeModel'.
Hide
jimmyshiau added a comment - 30/Oct/12 1:37 AM

Hi hshdev,

I finally found the reason!

By default the Tree component will render TreeModel's data into Treeitems when you open a tree node. When rendering, Tree component will try to sync the open status of each Treeitem, and the Tree component will check whether the data's path has been opened by getPath API or not.

And bascially the getPath API is implemented in the AbstractTreeModel, the implementation will go through each node to find the path (output an int array); when you open the Bond node ('Sophis e's child), the model will traverse each path of Bond node's children to sync open status, then go through each node in the model, it will then invoke isLeaf, getChildCount, getChild APIs several times.

To fine tune the performance, you have to implement your getPath method.
You can also refer to the following implementation for your "SimpleTreeModel":

public int[] getPath(String child) {
		char[] chars = child.toCharArray();
		List<Integer> p = new ArrayList<Integer>();
		for (int i = 0; i < chars.length; i++) {
			char c = chars[i];
			if (i == 0) {
				if (c == 'A')
					p.add(0);
				else if (c == 'B')
					p.add(1);
				else if (c == 'C')
					p.add(2);
				else if (c == 'D')
					p.add(3);
				else if (c == 'E')
					p.add(4);
			} else {
				p.add(Integer.parseInt(new Character(c).toString()));
			}
		}
		
		final Integer[] objs = p.toArray(new Integer[p.size()]);
		final int[] path = new int[objs.length];
		for (int i = 0; i < objs.length; i++)
			path[i] = objs[i].intValue();
		return path;
	}
}
Show
jimmyshiau added a comment - 30/Oct/12 1:37 AM Hi hshdev, I finally found the reason! By default the Tree component will render TreeModel's data into Treeitems when you open a tree node. When rendering, Tree component will try to sync the open status of each Treeitem, and the Tree component will check whether the data's path has been opened by getPath API or not. And bascially the getPath API is implemented in the AbstractTreeModel, the implementation will go through each node to find the path (output an int array); when you open the Bond node ('Sophis e's child), the model will traverse each path of Bond node's children to sync open status, then go through each node in the model, it will then invoke isLeaf, getChildCount, getChild APIs several times. To fine tune the performance, you have to implement your getPath method. You can also refer to the following implementation for your "SimpleTreeModel":
public int[] getPath(String child) {
		char[] chars = child.toCharArray();
		List<Integer> p = new ArrayList<Integer>();
		for (int i = 0; i < chars.length; i++) {
			char c = chars[i];
			if (i == 0) {
				if (c == 'A')
					p.add(0);
				else if (c == 'B')
					p.add(1);
				else if (c == 'C')
					p.add(2);
				else if (c == 'D')
					p.add(3);
				else if (c == 'E')
					p.add(4);
			} else {
				p.add(Integer.parseInt(new Character(c).toString()));
			}
		}
		
		final Integer[] objs = p.toArray(new Integer[p.size()]);
		final int[] path = new int[objs.length];
		for (int i = 0; i < objs.length; i++)
			path[i] = objs[i].intValue();
		return path;
	}
}
Hide
simbo added a comment - 26/Dec/12 6:06 PM

It would be helpful if the documentation of TreeModel could be updated to reflect this information that it is recommended that you override getPath to return a calculated path to root.

Show
simbo added a comment - 26/Dec/12 6:06 PM It would be helpful if the documentation of TreeModel could be updated to reflect this information that it is recommended that you override getPath to return a calculated path to root.
Hide
jumperchen added a comment - 27/Feb/13 3:07 AM

Bug fixed since 2/27/2013.

We found a way to optimize the tree to solve this issue.

Note that the getPath() method is good to override if the tree model contains a huge data.

Show
jumperchen added a comment - 27/Feb/13 3:07 AM Bug fixed since 2/27/2013. We found a way to optimize the tree to solve this issue. Note that the getPath() method is good to override if the tree model contains a huge data.

People

Vote (1)
Watch (2)

Dates

  • Created:
    26/Jul/12 8:49 AM
    Updated:
    27/Feb/13 3:09 AM
    Resolved:
    27/Feb/13 3:07 AM