001: package com.jamonapi.utils;
002:
003: import java.util.*;
004:
005: /**
006: * A NodeTree works with Compositenodes and LeafNodes to create heirarchical object trees. An example is a file system where directories
007: * can be viewed LeafNodes and directories can be viewed as CompoisteNodes. TreeNodes can be viewed as the top level of the tree structures.
008: *
009: * For JAMon monitors are grouped in heirarchical with the CompositeNodes being MonitorComposite. MonitorComposites can contain other
010: * MonitorComposites or LeafNodes which are the actual Monitors. Monitors are identified by monitor lables which are strings seperated
011: * by a common delimiter for example "MyApp.jsp.homePage". The first two levels of the heirarchy are CompositeNodes/MonitorComposites
012: * (MyApp and jsp), and the entry after the last delimiter is always the LeafNode (in this case a Monitor).
013: *
014: * Note that NodeTrees/CompositeNodes/LeafNodes can represent ANY hierarchical/tree structure. JAMon uses this generic pattern to
015: * model Monitors.
016: *
017: * NodeTree's use the first non-alphanumeric to represent the token delimiter. For example any combination of
018: * the following are considered token delimiters. (i.e. the following are all valid)
019: * pages.homepage (token delimeter is .)
020: * pages\homepage (token delimeter is \)
021: * pages\homepage.button (token delimeter is \ not .)
022: *
023: * note - Exception to the rule that token delimeters are the first non-alphanumeric characters are - and _.
024: * They are considered part of the variable name. A token delimeter can be any of the following: !@#$%^&*()+=[]\\{}|;':\",./<>?`~
025: *
026: **/
027:
028: public class NodeTree extends java.lang.Object {
029:
030: private CompositeNode compositeNodeFactory;
031:
032: public NodeTree(CompositeNode compositeNodeFactory) {
033: this .compositeNodeFactory = compositeNodeFactory;
034: }
035:
036: /** Return the highest level CompositeNode in the NodeTree. i.e. the entry point to all other CompositeNodes and LeafNodes.
037: * To use the file system analogy then the root node would be the root directory in Unix or c:\ in Windows.
038: **/
039: public CompositeNode getRootNode() {
040: return compositeNodeFactory.getRootNode();
041: }
042:
043: /** Return the NodeTree's LeafNode object that is identified by the locator and type. Also add it to the
044: * NodeTree.
045: *
046: * This method uses the StringTokenizer to parse the locator String. StringTokenizer is notoriously slow
047: * and this code could be replaced in the future.
048: *
049: * Sample Call:
050: * LeafNode leafNode=nodeTree.getLeafNode("MyApp.jsp.homePage", "type");
051: *
052: * The variable "type" is can be any string that is useful to the developer when creating a LeafNode.
053: *
054: * Note:
055: * getLeafNode(...) recursively calls getCompositeNode(...) for each NodeTree in the label. In the above example
056: * getCompositeNode(...) would be called once for "MyApp" and once for "jsp".
057: *
058: **/
059: public LeafNode getLeafNode(String locator, String type) {
060: String nodeName;
061: CompositeNode previousNode;
062: StringTokenizer st = getStringTokenizer(locator);
063: int lastToken = st.countTokens() - 1;
064: CompositeNode currentNode = getRootNode();
065:
066: // all nodes but the last one should be a composite
067: for (int i = 0; i < lastToken; i++) {
068: previousNode = currentNode;
069: nodeName = st.nextToken();
070:
071: currentNode = previousNode.getCompositeNode(nodeName);// creates if it doesn't exist
072: previousNode.addCompositeNode(nodeName, currentNode);
073: }
074:
075: // add the leaf node.
076: nodeName = st.nextToken();
077:
078: LeafNode leafNode = currentNode.getLeafNode(nodeName, type);
079:
080: currentNode.addLeafNode(nodeName, leafNode);
081: return leafNode;
082:
083: }
084:
085: /** Return the NodeTree's CompositeNode object that is identified by the locator. Also add it to the
086: * NodeTree.
087: *
088: * This method uses the StringTokenizer to parse the locator String. StringTokenizer is notoriously slow
089: * and this code could be replaced in the future.
090: *
091: * Sample Call:
092: * CompositeNode compositeNode=nodeTree.getCompositeNode("MyApp.jsp");
093: *
094: **/
095:
096: public CompositeNode getCompositeNode(String locator) {
097: StringTokenizer st = getStringTokenizer(locator);
098: ;
099: CompositeNode currentNode = getRootNode();
100: CompositeNode previousNode;
101:
102: while (st.hasMoreTokens()) {
103: previousNode = currentNode;
104: String nodeName = st.nextToken();
105:
106: currentNode = previousNode.getCompositeNode(nodeName);
107: previousNode.addCompositeNode(nodeName, currentNode);
108: }
109:
110: return currentNode;
111:
112: }
113:
114: private final boolean NODE_EXISTS = true;
115: private final boolean NODE_NO_EXISTS = false;
116:
117: /** Determine if the CompositeNode represented by the locator string exists.
118: * Sample Call:
119: * if (nodeTree.compositeNodeExists("MyApp.jsp"))
120: * ...
121: **/
122:
123: public boolean compositeNodeExists(String locator) {
124:
125: StringTokenizer st = getStringTokenizer(locator);
126: ;
127: CompositeNode currentNode = getRootNode();
128:
129: while (st.hasMoreTokens()) {
130: String nodeName = st.nextToken();
131:
132: if (currentNode.compositeNodeExists(nodeName))
133: currentNode = currentNode.getCompositeNode(nodeName);
134: else
135: return NODE_NO_EXISTS;
136:
137: }
138:
139: return NODE_EXISTS;
140:
141: }
142:
143: /** Determine if the leaf represented by the locator string exists.
144: * Sample Call:
145: * if (nodeTree.leafNodeExists("MyApp.jsp.homePage"))
146: * ...
147: **/
148: public boolean leafNodeExists(String locator) {
149: String nodeName;
150: StringTokenizer st = getStringTokenizer(locator);
151: ;
152: int lastToken = st.countTokens() - 1;
153: CompositeNode currentNode = getRootNode();
154:
155: // all nodes but the last one should be a composite
156: for (int i = 0; i < lastToken; i++) {
157: nodeName = st.nextToken();
158: if (currentNode.compositeNodeExists(nodeName))
159: currentNode = currentNode.getCompositeNode(nodeName);
160: else
161: return NODE_NO_EXISTS;
162: }
163:
164: // check that the last node is a leaf.
165: nodeName = st.nextToken();
166:
167: return currentNode.leafNodeExists(nodeName);
168:
169: }
170:
171: /** Determine if the Node represented by the locator string exists. The node can be either a CompositeNode or LeafNode.
172: * Sample Call:
173: * if (nodeTree.nodeExists("MyApp.jsp.homePage"))
174: * ...
175: **/
176:
177: public boolean nodeExists(String locator) {
178: return (compositeNodeExists(locator) || leafNodeExists(locator));
179: }
180:
181: private StringTokenizer getStringTokenizer(String locator) {
182: return new StringTokenizer(locator, getDelimiter(locator));
183: }
184:
185: // return the first non alpha numeric character and use it as a node delimiter
186: // i.e. if pages.home is passed then "." is returned or if pages/home is passed then "/" is returned
187: // if no delimiter is passed i.e. "pages" then an empty string will be used as the delimiter.
188: private String getDelimiter(String locator) {
189: for (int i = 0; i < locator.length(); i++) {
190: if (isTokenDelimiter(locator.charAt(i)))
191: return String.valueOf(locator.charAt(i));
192: }
193:
194: return "";
195: }
196:
197: private boolean isTokenDelimiter(char ch) {
198: //the following are valid delimiters
199: // !@#$%^&*()+=[]\\{}|;':\",./<>?`~
200: return !(Character.isLetterOrDigit(ch) || Character
201: .isWhitespace(ch));
202: }
203:
204: // add your data members here
205: }
|