Coverage Report - org.kuali.rice.krad.uif.container.TreeGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
TreeGroup
0%
0/108
0%
0/42
2.136
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.uif.container;
 17  
 
 18  
 import org.kuali.rice.core.api.util.tree.Node;
 19  
 import org.kuali.rice.core.api.util.tree.Tree;
 20  
 import org.kuali.rice.krad.uif.UifConstants;
 21  
 import org.kuali.rice.krad.uif.component.BindingInfo;
 22  
 import org.kuali.rice.krad.uif.component.Component;
 23  
 import org.kuali.rice.krad.uif.component.DataBinding;
 24  
 import org.kuali.rice.krad.uif.field.MessageField;
 25  
 import org.kuali.rice.krad.uif.util.ComponentUtils;
 26  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 27  
 import org.kuali.rice.krad.uif.view.View;
 28  
 
 29  
 import java.util.ArrayList;
 30  
 import java.util.List;
 31  
 import java.util.Map;
 32  
 
 33  
 /**
 34  
  * Group component that is backed by a <code>Tree</code> data structure and typically
 35  
  * rendered as a tree in the user interface
 36  
  *
 37  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 38  
  */
 39  
 public class TreeGroup extends Group implements DataBinding{
 40  
     private static final long serialVersionUID = 5841343037089286740L;
 41  
 
 42  
     private String propertyName;
 43  
     private BindingInfo bindingInfo;
 44  
 
 45  
     private Map<Class<?>, NodePrototype> nodePrototypeMap;
 46  
     private NodePrototype defaultNodePrototype;
 47  
 
 48  
     private Tree<Group, MessageField> treeGroups;
 49  
 
 50  
     private org.kuali.rice.krad.uif.widget.Tree tree;
 51  
 
 52  
     public TreeGroup() {
 53  0
         super();
 54  
 
 55  0
         treeGroups = new Tree<Group, MessageField>();
 56  0
     }
 57  
 
 58  
     /**
 59  
      * The following actions are performed:
 60  
      *
 61  
      * <ul>
 62  
      * <li>Set fieldBindModelPath to the collection model path (since the fields
 63  
      * have to belong to the same model as the collection)</li>
 64  
      * <li>Set defaults for binding</li>
 65  
      * <li>Calls view helper service to initialize prototypes</li>
 66  
      * </ul>
 67  
      *
 68  
      */
 69  
     @Override
 70  
     public void performInitialization(View view, Object model) {
 71  0
         setFieldBindingObjectPath(getBindingInfo().getBindingObjectPath());
 72  
 
 73  0
         super.performInitialization(view, model);
 74  
 
 75  0
         if (bindingInfo != null) {
 76  0
             bindingInfo.setDefaults(view, getPropertyName());
 77  
         }
 78  
 
 79  
         // TODO: set object path for prototypes equal to the tree group object path?
 80  
 
 81  0
         initializeNodePrototypeComponents(view, model);
 82  0
     }
 83  
 
 84  
     protected void initializeNodePrototypeComponents(View view, Object model) {
 85  0
         view.getViewHelperService().performComponentInitialization(view, model,
 86  
                 defaultNodePrototype.getLabelPrototype());
 87  0
         view.getViewHelperService().performComponentInitialization(view, model,
 88  
                 defaultNodePrototype.getDataGroupPrototype());
 89  
 
 90  0
         if (nodePrototypeMap != null) {
 91  0
             for (Map.Entry<Class<?>, NodePrototype> prototypeEntry : nodePrototypeMap.entrySet()) {
 92  0
                 NodePrototype prototype = prototypeEntry.getValue();
 93  0
                 if (prototype != null) {
 94  
 
 95  0
                     if (prototype.getLabelPrototype() != null) {
 96  0
                         view.getViewHelperService().performComponentInitialization(view, model,
 97  
                                 prototype.getLabelPrototype());
 98  
                     } else {
 99  0
                         throw new IllegalStateException("encountered null NodePrototype.labelPrototype");
 100  
                     }
 101  
 
 102  0
                     if (prototype.getDataGroupPrototype() != null) {
 103  0
                         view.getViewHelperService().performComponentInitialization(view, model,
 104  
                                 prototype.getDataGroupPrototype());
 105  
                     } else {
 106  0
                         throw new IllegalStateException("encountered null NodePrototype.dataGroupPrototype");
 107  
                     }
 108  
                 } else {
 109  0
                     throw new IllegalStateException("encountered null NodePrototype");
 110  
                 }
 111  0
             }
 112  
         }
 113  0
     }
 114  
 
 115  
     @Override
 116  
     public void performApplyModel(View view, Object model, Component parent) {
 117  0
         super.performApplyModel(view, model, parent);
 118  
 
 119  0
         buildTreeGroups(view, model);
 120  0
     }
 121  
 
 122  
     /**
 123  
      * Builds the components that will be rendered as part of the tree group
 124  
      *
 125  
      * <p>
 126  
      * The component tree group mirrors the tree data structure on the model. For each node of
 127  
      * the data structure, a corresponding <code>MessageField</code>  will be created for the node
 128  
      * label, and a <code>Group</code> component for the node data. These are placed into a new
 129  
      * node for the component tree. After the tree is built it is set as a property on the tree group
 130  
      * to be read by the renderer
 131  
      * </p>
 132  
      *
 133  
      * @param view - view instance the tree group belongs to
 134  
      * @param model - object containing the view data from which the tree data will be retrieved
 135  
      */
 136  
     protected void buildTreeGroups(View view, Object model) {
 137  
         // get Tree data property
 138  0
         Tree<Object, String> treeData = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
 139  
 
 140  
         // build component tree that corresponds with tree data
 141  0
         Tree<Group, MessageField> treeGroups = new Tree<Group, MessageField>();
 142  
 
 143  0
         String bindingPrefix = getBindingInfo().getBindingPrefixForNested();
 144  0
         Node<Group, MessageField> rootNode =
 145  
                 buildTreeNode(treeData.getRootElement(), bindingPrefix + /* TODO: hack */ ".rootElement", "root");
 146  0
         treeGroups.setRootElement(rootNode);
 147  
 
 148  0
         setTreeGroups(treeGroups);
 149  0
     }
 150  
 
 151  
     protected Node<Group, MessageField> buildTreeNode(Node<Object, String> nodeData, String bindingPrefix,
 152  
             String parentNode) {
 153  0
         if (nodeData == null) {
 154  0
             return null;
 155  
         }
 156  
 
 157  0
         Node<Group, MessageField> node = new Node<Group, MessageField>();
 158  0
         node.setNodeType(nodeData.getNodeType());
 159  
 
 160  0
         NodePrototype prototype = getNodePrototype(nodeData);
 161  
 
 162  0
         MessageField messageField = ComponentUtils.copy(prototype.getLabelPrototype(), parentNode);
 163  0
         ComponentUtils.pushObjectToContext(messageField, UifConstants.ContextVariableNames.NODE, nodeData);
 164  0
         messageField.setMessageText(nodeData.getNodeLabel());
 165  0
         node.setNodeLabel(messageField);
 166  
 
 167  0
         Group nodeGroup =
 168  
                 ComponentUtils.copyComponent(prototype.getDataGroupPrototype(), bindingPrefix + ".data", parentNode);
 169  0
         ComponentUtils.pushObjectToContext(nodeGroup, UifConstants.ContextVariableNames.NODE, nodeData);
 170  0
         node.setData(nodeGroup);
 171  
 
 172  0
         List<Node<Group, MessageField>> nodeChildren = new ArrayList<Node<Group, MessageField>>();
 173  
 
 174  0
         int childIndex = 0;
 175  0
         for (Node<Object, String> childDataNode : nodeData.getChildren()) {
 176  0
             String nextBindingPrefix = bindingPrefix + ".children[" + childIndex + "]";
 177  0
             Node<Group, MessageField> childNode = buildTreeNode(childDataNode, nextBindingPrefix, "_node_" + childIndex + ("root".equals(parentNode) ? "_parent_" : "_parent") + parentNode);
 178  
 
 179  0
             nodeChildren.add(childNode);
 180  
 
 181  
             // Don't forget about me:
 182  0
             ++childIndex;
 183  0
         }
 184  0
         node.setChildren(nodeChildren);
 185  
 
 186  0
         return node;
 187  
     }
 188  
 
 189  
     /**
 190  
      * This method gets the NodePrototype to use for the given Node
 191  
      */
 192  
     private NodePrototype getNodePrototype(Node<Object, String> nodeData) {
 193  0
         NodePrototype result = null;
 194  0
         if (nodeData != null && nodeData.getData() != null) {
 195  0
             Class<?> dataClass = nodeData.getData().getClass();
 196  0
             result = nodePrototypeMap.get(dataClass);
 197  
 
 198  
             // somewhat lame fallback - to do this right we'd find all entries that are assignable from the data class
 199  
             // and then figure out which one is the closest relative
 200  0
             if (result == null)
 201  0
                 for (Map.Entry<Class<?>, NodePrototype> prototypeEntry : nodePrototypeMap.entrySet()) {
 202  0
                     if (prototypeEntry.getKey().isAssignableFrom(dataClass)) {
 203  0
                         result = prototypeEntry.getValue();
 204  0
                         break;
 205  
                     }
 206  
                 }
 207  
         }
 208  0
         if (result == null)
 209  0
             result = defaultNodePrototype;
 210  0
         return result;
 211  
     }
 212  
 
 213  
     /**
 214  
      * @see org.kuali.rice.krad.uif.component.Component#getComponentsForLifecycle()
 215  
      */
 216  
     @Override
 217  
     public List<Component> getComponentsForLifecycle() {
 218  0
         List<Component> components = super.getComponentsForLifecycle();
 219  
 
 220  0
         components.add(tree);
 221  0
         addNodeComponents(treeGroups.getRootElement(), components);
 222  
 
 223  0
         return components;
 224  
     }
 225  
 
 226  
     /**
 227  
      * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
 228  
      */
 229  
     @Override
 230  
     public List<Component> getComponentPrototypes() {
 231  0
         List<Component> components = super.getComponentPrototypes();
 232  
 
 233  0
         if (defaultNodePrototype != null) {
 234  0
             components.add(defaultNodePrototype.getLabelPrototype());
 235  0
             components.add(defaultNodePrototype.getDataGroupPrototype());
 236  
         }
 237  
 
 238  0
         if (nodePrototypeMap != null) {
 239  0
             for (Map.Entry<Class<?>, NodePrototype> prototypeEntry : nodePrototypeMap.entrySet()) {
 240  0
                 NodePrototype prototype = prototypeEntry.getValue();
 241  0
                 if (prototype != null) {
 242  0
                     components.add(prototype.getLabelPrototype());
 243  0
                     components.add(prototype.getDataGroupPrototype());
 244  
                 }
 245  0
             }
 246  
         }
 247  
 
 248  0
         return components;
 249  
     }
 250  
 
 251  
     /**
 252  
      * Retrieves the <code>Component</code> instances from the node for building the nested
 253  
      * components list
 254  
      *
 255  
      * @param node - node to pull components from
 256  
      * @param components - list to add components to
 257  
      */
 258  
     protected void addNodeComponents(Node<Group, MessageField> node, List<Component> components) {
 259  0
         if (node != null) {
 260  0
             components.add(node.getNodeLabel());
 261  0
             components.add(node.getData());
 262  
 
 263  0
             for (Node<Group, MessageField> nodeChild : node.getChildren()) {
 264  0
                 addNodeComponents(nodeChild, components);
 265  
             }
 266  
         }
 267  0
     }
 268  
 
 269  
     public String getPropertyName() {
 270  0
         return propertyName;
 271  
     }
 272  
 
 273  
     public void setPropertyName(String propertyName) {
 274  0
         this.propertyName = propertyName;
 275  0
     }
 276  
 
 277  
     public BindingInfo getBindingInfo() {
 278  0
         return bindingInfo;
 279  
     }
 280  
 
 281  
     public void setBindingInfo(BindingInfo bindingInfo) {
 282  0
         this.bindingInfo = bindingInfo;
 283  0
     }
 284  
 
 285  
     /**
 286  
      * @return the defaultNodePrototype
 287  
      */
 288  
     public NodePrototype getDefaultNodePrototype() {
 289  0
         return this.defaultNodePrototype;
 290  
     }
 291  
 
 292  
     /**
 293  
      * @param defaultNodePrototype the defaultNodePrototype to set
 294  
      */
 295  
     public void setDefaultNodePrototype(NodePrototype defaultNodePrototype) {
 296  0
         this.defaultNodePrototype = defaultNodePrototype;
 297  0
     }
 298  
 
 299  
     /**
 300  
      * @return the nodePrototypeMap
 301  
      */
 302  
     public Map<Class<?>, NodePrototype> getNodePrototypeMap() {
 303  0
         return this.nodePrototypeMap;
 304  
     }
 305  
 
 306  
     /**
 307  
      * @param nodePrototypeMap the nodePrototypeMap to set
 308  
      */
 309  
     public void setNodePrototypeMap(Map<Class<?>, NodePrototype> nodePrototypeMap) {
 310  0
         this.nodePrototypeMap = nodePrototypeMap;
 311  0
     }
 312  
 
 313  
     public Tree<Group, MessageField> getTreeGroups() {
 314  0
         return treeGroups;
 315  
     }
 316  
 
 317  
     public void setTreeGroups(Tree<Group, MessageField> treeGroups) {
 318  0
         this.treeGroups = treeGroups;
 319  0
     }
 320  
 
 321  
     public org.kuali.rice.krad.uif.widget.Tree getTree() {
 322  0
         return tree;
 323  
     }
 324  
 
 325  
     public void setTree(org.kuali.rice.krad.uif.widget.Tree tree) {
 326  0
         this.tree = tree;
 327  0
     }
 328  
 }