View Javadoc

1   package com.sri.emo.dbobj.selectiontree;
2   
3   import com.jcorporate.expresso.core.db.DBException;
4   import com.sri.emo.dbobj.Node;
5   
6   import java.util.HashMap;
7   import java.util.Map;
8   
9   
10  /***
11   * Selects a tree of associated nodes.  Each node from the root is included
12   * in the tree if it has a strong relationship with its parent.
13   * <p/>
14   * each parent has an attribute MyChildren set with an Node[] array of children, and each child has an
15   * attribute set for its parent, a Node.
16   * <h4>Example Code</h4>
17   * <p/>
18   * <code><pre>
19   * Node n = new Node();
20   * //Set values
21   * <p/>
22   * TreeSelectionFactory selectionFactory = new TreeSelectionFactory(n);
23   * Map allNodesElegibleForDeletion = selectionFactory.getNodesInTree();
24   * <p/>
25   * </pre></code>
26   * </p>
27   *
28   * @author Michael Rimov, Larry Hamel
29   * @todo Rewrite the model as a series of decorative interfaces that have an
30   * in-memory tree design -- use that tree design to assign indentation
31   * levels.
32   */
33  public class TreeSelectionFactory {
34  
35      /***
36       * The root node of the traversal tree.
37       */
38      private final Node rootNode;
39  //    public static final String MY_PARENT_IS = "MyParentIs";
40      public static final String IS_PARENT_OF = "MyChildren";
41  
42      /***
43       * Construct a TreeSelectionFactory given the root ndoe.
44       *
45       * @param root Node
46       */
47      public TreeSelectionFactory(final Node root) {
48          rootNode = root;
49      }
50  
51  
52      /***
53       * Get ALL related nodes in tree beneath this node.
54       * <p/>
55       * side-effect: adds attribute 'level' with node level
56       * w/i tree to each node.
57       * side-effect: adds attribute 'MyParentIs' with parent node
58       * side-effect: adds attribute 'MyChildren' with array of Nodes of all children
59       * <p/>
60       * </p>
61       * <p/>
62       * uses optional INDENT attribute to determine formatting indentation
63       * </p>
64       *
65       * @return Map keyed by node id, pointing to the nodes in the tree.
66       * @throws DBException
67       * @todo get rid of side affect.
68       */
69      public Map getNodesInTree() throws DBException {
70          return getNodesInStronglyRelatedTree();
71      }
72  
73  
74      /***
75       * Get ALL related nodes in tree beneath this node EXCEPT types indicated
76       * for omission
77       * recurses into tree; side-effect: adds attribute 'level' with node level
78       * w/i tree to each node.
79       * <p/>
80       * uses optional INDENT attribute to determine formatting indentation
81       * </p>
82       *
83       * @throws DBException upon database access error.
84       */
85      public Map getNodesInStronglyRelatedTree() throws DBException {
86          Map outputMap = new HashMap();
87          rootNode.setAttribute(Node.INDENT, new Integer(1));
88          getNodesInStronglyRelatedTreeRecursive(rootNode, outputMap);
89          return outputMap;
90      }
91  
92  
93      /***
94       * Handles the recursive traversal through related nodes in a tree.
95       *
96       * Adds side-effect of an attribute on each node that is array of all immediate children. (null if none)
97       * @param outputMap Map
98       * @throws DBException
99       */
100     public static void getNodesInStronglyRelatedTreeRecursive(final Node currentNode,
101                                                         final Map outputMap) throws DBException {
102         Integer lastLevel = (Integer) currentNode.getAttribute(Node.INDENT);
103         assert lastLevel != null;
104         if (outputMap.containsKey(currentNode.getNodeId())) {
105             return;
106         }
107 
108         outputMap.put(currentNode.getNodeId(), currentNode);
109 
110         // fetch all the strongly-related nodes from relations at this level
111         Node[] stronglyRelated = currentNode.getStronglyRelatedNodes();
112 //        String curName = currentNode.getNodeTitle();
113         if (stronglyRelated.length > 0) {
114             // mark this node as a 'parent'
115             currentNode.setAttribute(IS_PARENT_OF, stronglyRelated);
116 
117             for (int j = 0; j < stronglyRelated.length; j++) {
118                 Node aRelatedNode = stronglyRelated[j];
119 
120                 // recurse if we haven't seen this node before
121                 if (outputMap.get(aRelatedNode.getNodeId()) == null) {
122                     // mark each child with parent
123 //                            aRelatedNode.setAttribute(MY_PARENT_IS, currentNode);
124                     aRelatedNode.setAttribute(Node.INDENT,
125                             new Integer(lastLevel.intValue() + 1));
126 
127                     getNodesInStronglyRelatedTreeRecursive(aRelatedNode, outputMap);
128                 }
129             }
130         }
131     }
132 
133 }