0

zk Tree - Load On Demand - performacne problem in live demo example

asked 2012-11-24 11:23:31 +0800

rmalinowski gravatar image rmalinowski
3

updated 2012-11-24 11:38:45 +0800

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 ?

delete flag offensive retag edit

9 Replies

Sort by ยป oldest newest

answered 2012-12-03 14:07:20 +0800

Mychal gravatar image Mychal
151 3

Did you solve your pb ? How did you realize your tree ?

Thank you

link publish delete flag offensive edit

answered 2012-12-03 15:51:40 +0800

bbrand gravatar image bbrand
98 3

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??

link publish delete flag offensive edit

answered 2012-12-03 16:29:42 +0800

bbrand gravatar image bbrand
98 3

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?

link publish delete flag offensive edit

answered 2012-12-03 18:10:45 +0800

bbrand gravatar image bbrand
98 3

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);
    }
}

link publish delete flag offensive edit

answered 2012-12-04 16:28:26 +0800

bbrand gravatar image bbrand
98 3

Bump ... anybody? Is this a bug or works as advertised?

link publish delete flag offensive edit

answered 2012-12-06 22:38:22 +0800

bbrand gravatar image bbrand
98 3

updated 2012-12-06 22:38:43 +0800

Bug report opened: ZK-1510

link publish delete flag offensive edit

answered 2012-12-26 01:34:17 +0800

jimmyshiau gravatar image jimmyshiau
4921 5
http://www.zkoss.org/ ZK Team

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)

link publish delete flag offensive edit

answered 2012-12-27 01:59:48 +0800

benbai gravatar image benbai
2228 6
http://www.zkoss.org

updated 2012-12-27 04:57:02 +0800

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

link publish delete flag offensive edit

answered 2013-01-30 15:47:19 +0800

bbrand gravatar image bbrand
98 3

updated 2013-01-30 15:51:30 +0800

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.

link publish delete flag offensive edit
Your reply
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2012-11-24 11:23:31 +0800

Seen: 352 times

Last updated: Jan 30 '13

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More