10 Replies Latest reply on Jun 16, 2008 8:10 AM by sendewelt

    RecursiveTreeNodesAdapter and RowKeys

    hendrikbeck

      Hi!

      I am displaying a tree using the RecursiveTreeNodesAdapter. Now I wanted to expand nodes programmatically. I also found some posts that made IMO sense and pointed me into the right direction. But now I am stuck with the following question:

      The approaches I found are using RowKeys to select nodes within the tree. Basically they are doing the following:

      public String expandNode() {
       Node node = getNode(nodeId);
       ArrayList<Integer> path = new ArrayList<Integer>();
       while (node != null) {
       path.add(node.getId());
       node = (Node) node.getParent();
       }
       Collections.reverse(path);
       path.remove(0); // dont expand rootnode, it is not displayed
      
       for (int i = 0; i < path.size(); i++) {
       ArrayList<Integer> list = new ArrayList<Integer>();
       for (int n = 0; n <= i; n++) {
       list.add(path.get(n));
       }
      
       ListRowKey key = new ListRowKey(list);
       try {
       ((HtmlTree) tree).queueNodeExpand(key);
       } catch (IOException e) {
       e.printStackTrace();
       }
       }
      
       return null;
       }


      As I understood it, the row keys are coming from the identifier passed in TreeNode.addChild(identifier, node). But my nodes don't implement TreeNode, they are just a recursive POJO-like structure. This is why I used the recursiveTreeNodesAdapter (which I was really amazed about, by the way).

      So to be able to use these row keys, do I have to implement a node model where my nodes are inheriting TreeNode? Or am I missing something here?


      Or maybe in other words: in my bean I have a binding of the HtmlTree and in the method that should expand one single node I also have the node object available. But HtmlTree.queueNodeExpand() expects a TreeRowKey as parameter. So how do I get from my node to that key?

      Any help or pointers are appreciated...

      Thanks in advance and regards
      Hendrik

        • 1. Re: RecursiveTreeNodesAdapter and RowKeys
          jbuechel

          Hi hendrik

          Did you find a solution for this?
          I'm working on similar issues and using RecursiveTreeNodesAdapter as well.

          Do you know about the TreeStateAdvisor interface?
          Here some snippets:

          @Name("organigramTreeStateAdvisor")
          public class OrganigramTreeStateAdvisor implements TreeStateAdvisor {
          ...
           @Override
           public Boolean adviseNodeOpened(UITree uiTree) {
          
           TreeRowKey treeRowKey = (TreeRowKey) uiTree.getRowKey();
           if (treeRowKey == null || treeRowKey.depth() <= 1)
           return Boolean.TRUE;
           else
           return null;
           }
          
           @Override
           public Boolean adviseNodeSelected(UITree uiTree) {
           Object rowData = uiTree.getRowData();
          
           if (rowData == organigramTreeQueryModelSelection)
           return Boolean.TRUE;
           else
           return null;
           }
          ...
          


          <rich:tree switchType="ajax" stateAdvisor="#{organigramTreeStateAdvisor}" ajaxSubmitSelection="true"
          

          Hope that helps somehow..

          What i have to figure out in my case, is the "cheapest" way how to expand a certain node even it haven't been lazy loaded from the database.


          • 2. Re: RecursiveTreeNodesAdapter and RowKeys
            hendrikbeck

            Hi!

            Well, I "heard" about it before, but I didn't really know what it's for since now. And no, I didn't find a solution, actually I just delayed the problem until now ;-)

            Ok, as far as I understood this TreeStateAdvisor only decides, wether the tree should be rerendered in the next request, doesn't it? But by using the function UITree.queueNodeExpanded() this should work well in the background without having to customize the Advisor. Am I about right?


            What I need to to actually expand a certain node. So I would like to call

            UITree.queueNodeExpand(TreeRowKey)


            But I can't figure out how to determine that TreeRowKey. I have my own data model, which is actually nothing else than a recursive structure of Java objects, something like that:

            public class MyNode() {
            
             Node[] getChildren();
            
             Node getParent();
            
            }


            Inside that class there are some more methods, e.g. to load the child nodes lazily and stuff like that.

            So basically I know what node to expland. Since I stored a reference to the root node I can also iterate through the nodes. But that last step is unclear, how to get from the node object to the RowKey in order to tell the tree to expand that node.

            Or maybe another way, but it seems to me that the only appropriate way is to call UITree.queueExpandNode(TreeRowKey)...

            I am stuck... Or did I just misunderstand you and that's the same problem you have right now?

            Thanks a lot and greetz
            Hendrik

            • 3. Re: RecursiveTreeNodesAdapter and RowKeys
              jbuechel

              You're right, finally i just started to work on the quite same issue as you are talking about..

              Until now I needed those methods for expanding or selecting certain nodes during rendering the tree.
              These methods are not called for tree nodes which are collapsed, afaik.

              I'll do some more research on this and let you know when i've found something helpful.
              When you've got some news please let me know as well..

              Btw:
              There is a TreeState class which provides the method:

              treeState.expandNode(tree, rowKey);

              ..but it also requires the rowKey.


              • 4. Re: RecursiveTreeNodesAdapter and RowKeys
                jbuechel

                There could be a way to expand nodes by adding the objects to expand to a list as following:

                This method is executed when expand action is executed: (argument organigramNode = node object to expand)

                public void generateExpandedNodeList(OrganigramNode organigramNode) {
                 int MAX_TREE_SEARCH_LEVEL = 100;
                
                 expandedTreeNodes = new ArrayList();
                 expandedTreeNodes.add(organigramNode);
                 for (int i = 0; i < MAX_TREE_SEARCH_LEVEL
                 && organigramNode.getMissionNode() == null; i++) {
                
                 organigramNode = organigramNode.getParentNode();
                 expandedTreeNodes.add(organigramNode);
                 }
                 if (organigramNode.getMissionNode() == null)
                 throw new RootNodeNotFoundException(
                 "No mission (root) node found in tree.");
                
                 expandedTreeNodes.add(organigramNode.getMissionNode());
                 }
                


                TreeStateAdvisor:
                public Boolean adviseNodeOpened(UITree uiTree) {
                 if(expandedTreeNodes == null || expandedTreeNodes.isEmpty())
                 return null;
                
                 if (expandedTreeNodes.contains(uiTree.getRowData()))
                 return Boolean.TRUE;
                 else
                 return null;
                 }
                


                But...
                The relevant tree nodes indeed seems to be expanded (iconExpanded is shown) but the children are not displayed. If the expand action is executed again the direct children are displayed and so on..

                Maybe Nick could help with this issue?


                • 5. Re: RecursiveTreeNodesAdapter and RowKeys
                  ronanker

                  Hi all,

                  But...
                  The relevant tree nodes indeed seems to be expanded (iconExpanded is shown) but the children are not displayed. If the expand action is executed again the direct children are displayed and so on..

                  I have exactly the same issue.

                  I'm trying to use StateAdvisor to expand/collapse nodes of my tree.
                  My nodes are just POJOs with a state (collapse or not).

                  Did you find a way to make this works ?



                  • 6. Re: RecursiveTreeNodesAdapter and RowKeys
                    jbuechel

                    Hmm, that's been quite a long time ago.. I don't remember what did the trick..

                    Did you try to rerender not only the tree it self, but the container which is containing the tree?
                    And what about the scope of the tree model?

                    Expand tree node call:

                    <a4j:commandButton id="#{fwcTooltipMessageKey}" action="#{militaryRoleQueryDataModelManager.expandOrganigramNode}"
                    image="#{fwcResource.resourcePathWeb}#{theme.img}tree/tree.png"
                    reRender="queryTreeContainerId, #{dataTableComponentId}">
                    </a4j:commandButton>


                    tree:
                    <a4j:form>
                    
                     <rich:tree id="organigramTreeQueryId" switchType="ajax" showConnectingLines="false"
                     ajaxSingle="false" ajaxSubmitSelection="true" stateAdvisor="#{organigramTreeQueryStateAdvisor}"
                     reRender="militaryRoleQueryTableId"
                     icon="#{fwcResource.resourcePathWeb}#{theme.img}tree/folder_closed.png"
                     iconCollapsed="#{fwcResource.resourcePathWeb}#{theme.img}tree/expand.gif"
                     iconExpanded="#{fwcResource.resourcePathWeb}#{theme.img}tree/collapse.gif"
                     iconLeaf="#{fwcResource.resourcePathWeb}#{theme.img}tree/folder_closed.png">
                    
                     <rich:nodeSelectListener type="com.frox.aafn.ui.tree.OrganigramNodeQuerySelectListener" />
                    
                     <rich:treeNodesAdaptor id="missionNode" nodes="#{organigramQueryTreeModel}" var="missionNode">
                    
                     <rich:treeNode>
                     <h:outputText value="#{missionNode.identifier}" />
                     </rich:treeNode>
                    
                     <rich:recursiveTreeNodesAdaptor id="organigramNode" var="organigramNode"
                     roots="#{organigramTreeQueryModelManager.getVisibleOrganigramNodes(missionNode)}"
                     nodes="#{organigramNode.childNodesAsList}">
                    
                     <rich:treeNode>
                     <h:outputText value="#{organigramNode.name}" />
                     </rich:treeNode>
                    
                     </rich:recursiveTreeNodesAdaptor>
                    
                     </rich:treeNodesAdaptor>
                     </rich:tree>
                    
                    </a4j:form>
                    


                    Hope this helps you somehow..

                    • 7. Re: RecursiveTreeNodesAdapter and RowKeys
                      ronanker


                      Thanks for the answer,

                      I compared your code an mine and I transformed ajaxSingle="true" in ajaxSingle="false".

                      My tree is now working as expected.

                      • 8. Re: RecursiveTreeNodesAdapter and RowKeys
                        sendewelt

                        Hello,

                        i have the problem that i want to expand a certaon node in a tree, when a link is clicked which is outside the tree.
                        is that possible?
                        @jbuechel: could you provide impl of militaryRoleQueryDataModelManager.expandOrganigramNode?
                        maybe that would be helpful for me. i already tried may things but i did not get it woking so far.

                        br,

                        andi

                        • 9. Re: RecursiveTreeNodesAdapter and RowKeys
                          jbuechel

                          Hi sendewelt

                          expandOrganigramNode() method:

                          public void expandOrganigramNode() throws TreeException {
                           log.debug("expandOrganigramNode() called");
                          
                           locatedOrganigramNode = militaryRoleQueryDataModelSelection.getOrganigramNode();
                          
                           this.expandOrganigramNode(locatedOrganigramNode);
                           }
                          
                           public void expandOrganigramNode(OrganigramNode organigramNode) throws TreeException {
                           log.debug("locateOrganigramNode() called");
                          
                           locatedOrganigramNode = organigramNode;
                          
                           this.generateExpandedNodeList(locatedOrganigramNode);
                          
                           this.refreshModel(locatedOrganigramNode);
                          
                           this.clearExample();
                           this.toggleTreeDisplay(TREE_DISPLAY.AS_TREE);
                           }


                          Please note the injected/outjected Seam-Variable "expandedTreeNodes".

                          This method is beeing called from expandOrganigramNode():
                          /**
                           * Generates a list of all objects which has to be expanded in the tree.
                           *
                           * @param organigramNode
                           */
                           public void generateExpandedNodeList(OrganigramNode organigramNode) throws TreeException {
                           int MAX_TREE_SEARCH_LEVEL = 100;
                          
                           expandedTreeNodes = new ArrayList();
                           for (int i = 0; i < MAX_TREE_SEARCH_LEVEL && organigramNode.getMissionNode() == null; i++) {
                          
                           organigramNode = organigramNode.getParentNode();
                           expandedTreeNodes.add(organigramNode);
                           }
                           if (organigramNode.getMissionNode() == null)
                           throw new RootNodeNotFoundException("No mission (root) node found in tree.");
                          
                           expandedTreeNodes.add(organigramNode.getMissionNode());
                           }
                          


                          This is the implementation of the state advisor (organigramTreeQueryStateAdvisor):

                          package com.frox.aafn.ui.tree;
                          
                          import java.io.Serializable;
                          import java.util.Collection;
                          
                          import org.jboss.seam.ScopeType;
                          import org.jboss.seam.annotations.In;
                          import org.jboss.seam.annotations.Logger;
                          import org.jboss.seam.annotations.Name;
                          import org.jboss.seam.annotations.Scope;
                          import org.jboss.seam.log.Log;
                          import org.richfaces.component.UITree;
                          import org.richfaces.component.state.TreeState;
                          import org.richfaces.component.state.TreeStateAdvisor;
                          
                          import com.frox.aafn.model.entity.OrganigramNode;
                          
                          @Scope(ScopeType.CONVERSATION)
                          @Name("organigramTreeQueryStateAdvisor")
                          public class OrganigramTreeQueryStateAdvisor implements TreeStateAdvisor, Serializable {
                          
                           private static final long serialVersionUID = 8663828659543548189L;
                          
                           @Logger
                           Log log;
                          
                           @In(required = false)
                           private OrganigramNode locatedOrganigramNode;
                          
                           @In(required = false)
                           private Collection expandedTreeNodes;
                          
                           private TreeState treeState;
                          
                           /*
                           * (non-Javadoc)
                           *
                           * @see org.richfaces.component.state.TreeStateAdvisor#adviseNodeOpened(org.richfaces.component.UITree)
                           */
                           @Override
                           public Boolean adviseNodeOpened(UITree uiTree) {
                           if (expandedTreeNodes == null || expandedTreeNodes.isEmpty())
                           return null;
                          
                           if (expandedTreeNodes.contains(uiTree.getRowData()))
                           return Boolean.TRUE;
                           else
                           return null;
                           }
                          
                           /*
                           * (non-Javadoc)
                           *
                           * @see org.richfaces.component.state.TreeStateAdvisor#adviseNodeSelected(org.richfaces.component.UITree)
                           */
                           @Override
                           public Boolean adviseNodeSelected(UITree uiTree) {
                           Object node = uiTree.getRowData();
                           if (locatedOrganigramNode == null || node == null)
                           return null;
                           if (node.equals(locatedOrganigramNode)) {
                           return Boolean.TRUE;
                           } else
                           return null;
                           }
                          
                           public TreeState getTreeState() {
                           return treeState;
                           }
                          
                           public void setTreeState(TreeState treeState) {
                           this.treeState = treeState;
                           }
                          
                          }
                          


                          Does this help? Good luck.. ;-)



                          • 10. Re: RecursiveTreeNodesAdapter and RowKeys
                            sendewelt

                            Hi jbuechel,

                            thank u very much for your quick reply....

                            br,

                            Andi