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