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
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
111 Node[] stronglyRelated = currentNode.getStronglyRelatedNodes();
112
113 if (stronglyRelated.length > 0) {
114
115 currentNode.setAttribute(IS_PARENT_OF, stronglyRelated);
116
117 for (int j = 0; j < stronglyRelated.length; j++) {
118 Node aRelatedNode = stronglyRelated[j];
119
120
121 if (outputMap.get(aRelatedNode.getNodeId()) == null) {
122
123
124 aRelatedNode.setAttribute(Node.INDENT,
125 new Integer(lastLevel.intValue() + 1));
126
127 getNodesInStronglyRelatedTreeRecursive(aRelatedNode, outputMap);
128 }
129 }
130 }
131 }
132
133 }