-
FEATURED COMPONENTS
First time here? Check out the FAQ!
Hi,
( I'm using zk version 6.5.0 - from maven repository
How to replicate issue: run live example from http://www.zkoss.org/zkdemo/tree/load_on_demand )
zk provided example tree load on demand http://www.zkoss.org/zkdemo/tree/load_on_demand
and I'm trying to understand how BinaryTreeModel (from example) works .
first I will write my conclusion (for those who don't want to read all)
tree widget from zk live demo after first call (not opened any nodes)
calls 14 methods from BinaryTreeModel
with first level node opened - calls 45 methods from BinaryTreeModel
with second level node opened - calls 900 methods!!!
with next level node opened - calls 1400 methods!!!
and so on...
my question is:
how to implement Model for Tree for BigData Loaded From DataBase which will not kill DataBase
or
where is my mistake in following reasoning
And now how I found it:
I didn't change BinaryTreeModel class,
I only added to all methods echos for logging method calls sequence
After my changes the example class BinaryTreeModel looks like this:
public class BinaryTreeModel<T> extends AbstractTreeModel<T> implements TreeSelectableModel { private static final long serialVersionUID = 7822729366554623684L; private ArrayList<T> _tree = null; public BinaryTreeModel(ArrayList<T> tree) { super(tree.get(0)); _tree = tree; } public T getChild(Object parent, int index) { Clients.log("getChild index: "+index+" parent: "+parent); int i = _tree.indexOf(parent) * 2 + 1 + index; if (i >= _tree.size()) { return null; } else { return _tree.get(i); } } public int getChildCount(Object parent) { Clients.log("getChildCount "+parent); int count = 0; if (getChild(parent, 0) != null) { count++; } if (getChild(parent, 1) != null) { count++; } return count; } public boolean isLeaf(Object node) { Clients.log("isLeaf "+node); return (getChildCount(node) == 0); } @Override public int getIndexOfChild(Object arg0, Object arg1) { Clients.log("getIndexOfChild arg0: "+arg0 + " arg1: "+arg1); return 0; } }
ok, after this modification I started application.
after open zul i have trace log (14 lines, one line = one method call):
getChildCount 0 getChild index: 0 parent: 0 getChild index: 1 parent: 0 getChild index: 0 parent: 0 isLeaf 1 getChildCount 1 getChild index: 0 parent: 1 getChild index: 1 parent: 1 getChild index: 1 parent: 0 isLeaf 2 getChildCount 2 getChild index: 0 parent: 2
then I'm opening first node (node 1)
tracelog (45 lines, one line = one method call):
isLeaf 0 getChildCount 0 getChild index: 0 parent: 0 getChild index: 1 parent: 0 getChildCount 0 getChild index: 0 parent: 0 getChild index: 1 parent: 0 getChild index: 0 parent: 0 isLeaf 1 getChildCount 1 getChild index: 0 parent: 1 getChild index: 1 parent: 1 getChildCount 1 getChild index: 0 parent: 1 getChild index: 1 parent: 1 isLeaf 1 getChildCount 1 getChild index: 0 parent: 1 getChild index: 1 parent: 1 getChildCount 1 getChild index: 0 parent: 1 getChild index: 1 parent: 1 getChild index: 0 parent: 1 isLeaf 3 getChildCount 3 getChild index: 0 parent: 3 getChild index: 1 parent: 3 getChild index: 1 parent: 1 isLeaf 4 getChildCount 4 getChild index: 0 parent: 4 getChild index: 1 parent: 4
AND MAGIC STARTS HERE:
then I'm opening second node (node 3)
tracelog has 950 lines
some part of it:
(...) isLeaf 255 getChildCount 255 getChild index: 0 parent: 255 getChild index: 1 parent: 255 getChildCount 255 getChild index: 0 parent: 255 getChild index: 1 parent: 255 getChild index: 0 parent: 255 isLeaf 511 getChildCount 511 getChild index: 0 parent: 511 getChild index: 1 parent: 511 getChild index: 1 parent: 255 isLeaf 512 getChildCount 512 getChild index: 0 parent: 512 getChild index: 1 parent: 512 getChild index: 1 parent: 127 isLeaf 256 getChildCount 256 getChild index: 0 parent: 256 getChild index: 1 parent: 256 getChildCount 256 getChild index: 0 parent: 256 getChild index: 1 parent: 256 getChild index: 0 parent: 256 isLeaf 513 getChildCount 513 getChild index: 0 parent: 513 getChild index: 1 parent: 513 getChild index: 1 parent: 256 isLeaf 514 getChildCount 514 getChild index: 0 parent: 514 getChild index: 1 parent: 514 getChild index: 1 parent: 63 isLeaf 128 getChildCount 128 getChild index: 0 parent: 128 getChild index: 1 parent: 128 getChildCount 128 getChild index: 0 parent: 128 getChild index: 1 parent: 128 getChild index: 0 parent: 128 isLeaf 257 (...)
when next node opened (node 7)
tracelog has 1400 lines
next node (node 15)
trace log has 1700 lines
and so on...
why there is so many method calls for this simple tree model ?
I noticed this as well in the app I am building and it has me stumped, it seems to bounce around for a long time. Strangely enough if you have only 1 node "open" it seems to work as advertised. So open-close-open does not have this behavior. Bug??
Yes, I discovered this attempting the same thing: build a complicated load-on-demand tree.
In my case the data in the child branch is dependent on the data in the parent node, and the above issue creates HUGE problems. I have this running in 5.0.x without problems but now porting to 6.5 and enhancing the app is creating lots of "challenges" around the tree.
What we need is:
- the ability to detect an open Event on a child, capture the contents of the parent, populate the child branch and hand it over the TreeModel to render.
Suggestions? Workarounds?
Here is my code:
ZUL:
<?xml version="1.0" encoding="UTF-8"?> <zk xmlns="http://www.zkoss.org/2005/zul"> <window id="mainWindow" width="100%" height="100%" apply="zkoss.test.BBTreeController"> <borderlayout> <!-- header frame --> <north title="A Dynamic Tree v6" vflex="true"> <grid fixedLayout="true" width="100%"> <columns> <column label="" width="200px"/> <column label="" width="100%"/> </columns> <rows> <row> <label value="Click to start"/> <button id="startButton" label="START"/> </row> </rows> </grid> </north> <!-- navigation frame --> <center flex="true"> <tree id="navTree" width="100%" vflex="true" autopaging="true"> <treecols sizable="true"> <treecol label="Item" width="10%"/> <treecol label="Description" width="90%"/> </treecols> </tree> </center> <!-- edit frame --> <south size="45%" vflex="true"> <tabbox id="editFrame" height="100%"> <tabs> <tab id="tab1" label="TAB 1" /> <tab id="tab2" label="TAB 2" /> <tab id="tab3" label="TAB 3" /> </tabs> <tabpanels> <!-- edit panel #1 --> <tabpanel> <grid fixedLayout="true" width="100%"> <columns> <column label="" width="200px"/> <column label="" width="100%"/> </columns> <rows> <row> <label value="Field 1.1"/> <textbox id="f1"/> </row> <row> <label value="Field 1.2"/> <textbox id="f2"/> </row> <row> <label value="Field 1.3"/> <textbox id="f3"/> </row> <row> <label value="Field 1.4"/> <textbox id="f4"/> </row> </rows> </grid> </tabpanel> <!-- edit panel #2 --> <tabpanel> <grid fixedLayout="true" width="100%"> <columns> <column label="" width="200px"/> <column label="" width="100%"/> </columns> <rows> <row> <label value="Field 2.1"/> <textbox id="f21"/> </row> <row> <label value="Field 2.2"/> <textbox id="f22"/> </row> <row> <label value="Field 2.3"/> <textbox id="f23"/> </row> <row> <label value="Field 2.4"/> <textbox id="f24"/> </row> </rows> </grid> </tabpanel> </tabpanels> </tabbox> </south> </borderlayout> </window> </zk>
Controller:
/* * BBTreeController: demo a tree */ package zkoss.test; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.util.Composer; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; import org.zkoss.zul.Tree; import org.zkoss.zul.Window; public class BBTreeController extends GenericForwardComposer implements Composer { Window mainWindow; Button startButton; Tree navTree; // BBTreeModel treeModel = new BBTreeModel(); @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); } public void onClick$startButton(Event event) { BBTreeRenderer lineItem = new BBTreeRenderer(); navTree.setItemRenderer(lineItem); treeModel.buildRoot(1, ""); navTree.setModel(treeModel); } }
TreeModel:
/* * Demo TreeModel, this should infinitly explode any branch to the same number of branches * */ package zkoss.test; import java.util.ArrayList; import org.zkoss.zul.AbstractTreeModel; public class BBTreeModel extends AbstractTreeModel<Object> { private ArrayList rootList = new ArrayList(); // the root of the tree, rootMode tells you what this is // private Integer rootMode; private String rootFilter; // public BBTreeModel() { super("Root"); } public void buildRoot(int r, String f) { // // The tree is represented via a set of ArrayList's (rootList) and subsequent // .. sub lists. Only the root list is populated initially and the children of a parent (if any) // .. are built on demand when they are expanded. // rootList.clear(); // rootMode = r; rootFilter = f; // if (rootMode == 1) { // default entry point for (int i = 0; i < 21; ++i) { // build dummy list // // Each item on the tree (leaf/branch) is again an arraylist (treeItem) that is taken apart in the // .. TreeItemRenderer and used to populate the columns of the tree. This list also contains // .. info about the level (depth) of the tree and if the item is a leaf/branch. These // .. additional entries in the list are not redendered // // treeItem - List: // 0 = RENDERED: id that the user uses to reconize this item (for example order number) // 1 = RENDERED: description of the item // 2 = INFO: tree item type // 3 = INFO: how many children does the item have (0 = leaf) // 4 = INFO: arraylist: cache of the expanded child // ArrayList treeItem = new ArrayList(); treeItem.add(i); treeItem.add("this is item # " + i); treeItem.add("ORDER"); treeItem.add(4); treeItem.add(new ArrayList(0)); rootList.add(treeItem); } } } @Override public Object getChild(Object parent, int index) { System.out.println("===== @getChild " + parent + " | " + index + " | "); System.out.flush(); // ArrayList cList = new ArrayList(); // if (parent.getClass() == String.class) { // root = string cList = (ArrayList) rootList.get(index); } else { // all others should be an arraylist ArrayList pList = (ArrayList) parent; String pType = (String) pList.get(2); // if (pType.equals("ORDER")) { Integer cSize = (Integer) pList.get(3); ArrayList cCache = (ArrayList) pList.get(4); if (cSize > 0 && cCache.isEmpty()) { // build branch cache System.out.println("\t\tbuildChild"); ArrayList treeItem1 = new ArrayList(); treeItem1.add("Effort"); treeItem1.add("The effort spent on order " + pList.get(0)); treeItem1.add("EFFORT"); treeItem1.add(0); treeItem1.add(new ArrayList(0)); cCache.add(treeItem1); // ArrayList treeItem2 = new ArrayList(); treeItem2.add("Expenses"); treeItem2.add("The expenses spent on order " + pList.get(0)); treeItem2.add("EXPENSES"); treeItem2.add(2); treeItem2.add(new ArrayList(0)); cCache.add(treeItem2); // ArrayList treeItem3 = new ArrayList(); treeItem3.add("Invoiced"); treeItem3.add("The invoices booked on order " + pList.get(0)); treeItem3.add("INVOICES"); treeItem3.add(2); treeItem3.add(new ArrayList(0)); cCache.add(treeItem3); // ArrayList treeItem4 = new ArrayList(); treeItem4.add("Planning"); treeItem4.add("What has been planned on order " + pList.get(0)); treeItem4.add("PLANNED"); treeItem4.add(0); treeItem4.add(new ArrayList(0)); cCache.add(treeItem4); } cList = (ArrayList) cCache.get(index); } } return cList; } @Override public int getChildCount(Object parent) { Integer cc = 0; if (parent.getClass() == String.class) { // root entry cc = rootList.size(); } else { // should be an ArrayList ArrayList cList = (ArrayList) parent; if (cList.isEmpty()) { cc = 0; } else { cc = (Integer) cList.get(3); } } return cc.intValue(); } @Override public boolean isLeaf(Object node) { return this.getChildCount(node) == 0; } /* * @Override public int getIndexOfChild(Object parent, Object child) { * String data = (String) child; int i = data.lastIndexOf('.'); return * Integer.parseInt(data.substring(i + 1)); } * */ }
Finally, the TreeItemRenderer
package zkoss.test; import java.util.ArrayList; import org.zkoss.zul.Treecell; import org.zkoss.zul.Treeitem; import org.zkoss.zul.TreeitemRenderer; import org.zkoss.zul.Treerow; /** * * @author brand */ public class BBTreeRenderer implements TreeitemRenderer { @Override public void render(Treeitem treeItem, Object data, int i) throws Exception { ArrayList orderData = (ArrayList) data; Treecell item = new Treecell(orderData.get(0).toString()); Treecell descriptor = new Treecell((String) orderData.get(1)); Treerow treeRow = null; // if (treeItem.getTreerow() == null) { treeRow = new Treerow(); treeRow.setParent(treeItem); } else { treeRow = treeItem.getTreerow(); treeRow.getChildren().clear(); } // item.setParent(treeRow); descriptor.setParent(treeRow); treeItem.setOpen(false); } }
Bug report opened: ZK-1510
Hi all,
please refer to my comment
jimmyshiau added a comment - 30/Oct/12 1:37 AM
http://tracker.zkoss.org/browse/ZK-1278
the getPath API is implemented in the AbstractTreeModel, the implementation will go through each node to find the path (output an int array)
http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/AbstractTreeModel.html#getPath(E)
Hi rmalinowski,
AbstractTreeModel only provides a general implementation which can fit any type of node and data model, to work with Specific Node and Specific Model smoothly, you may need to customize some method.
e.g., for the binary tree model, you may implement the getPath as below to solve the performance issue:
... public T getParent (Object child) { int index = _tree.indexOf(child); int i = 0; if (index >= 2) { i = (index - 1) / 2; if (i >= 0 && i < _tree.size()) { return _tree.get(i); } } return null; } .... public int[] getPath (T child) { int[] path = null; List<Integer> bPath = new ArrayList<Integer>(); T node = child; while (node != null) { bPath.add(0, (_tree.indexOf(node) - 1) % 2); node = getParent(node); } path = new int[bPath.size()]; for (int j = 0; j < bPath.size(); j++) { path[ j ] = bPath.get(j); } return path; }
Regards,
Ben
The issue was resolved ultimately. AbstractTreeModel is some what ham strung as it does not track where it is and needs to walk through the complete tree to do this.
The answer is to write you own getPath method overriding the one in the the AbstractTreeModel. Here you need to offer up for each node a comma separated list of indexes explaining where this node sits on the tree. The first node = 0 then counts up, the first child of a parent starts at 0 etc. So 3,2,1 = the 4th node you see on the tree, then the third line and then the second line. I kind of hate counting from zero, to me it is counter intuitive. The first line = 0 just never makes sense, o well.
Here is an example based on my code above. Note that each node in my example is an arraylist and that in a bespoke renderer I render certain items in that list. One of the items in the list (6) is my path tracker.
@Override
public int[] getPath(Object node) {
ArrayList cList = (ArrayList) node; // get the list on the node
String path = (String) cList.get(6); // get the path as documented
String[] pl = path.split(","); // to string[], and then integer[]
int[] p = new int[pl.length];
for (int index = 0; index < pl.length; index++) {
p[index] = new Integer(pl[index]).intValue();
}
return p;
}
EDIT: As you see it does not be to a comma seperated string of indexes, that is my implementations. I kind of implied that but misstated. The method returns an array of integers documenting the path, and there are multiple ways to achieve this, of which the above is just one.
Asked: 2012-11-24 11:23:31 +0800
Seen: 353 times
Last updated: Jan 30 '13