1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.util;
47
48 import org.codehaus.groovy.runtime.InvokerHelper;
49
50 import groovy.xml.QName;
51
52 import java.io.PrintWriter;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Map;
58
59 /***
60 * Represents an arbitrary tree node which can be used for structured metadata which can be any arbitrary XML-like tree.
61 * A node can have a name, a value and an optional Map of attributes.
62 * Typically the name is a String and a value is either a String or a List of other Nodes.
63 * Though the types are extensible to provide a flexible structure.
64 * e.g. you could use a QName as the name which includes a namespace URI and a local name. Or a JMX ObjectName etc.
65 * So this class can represent metadata like {foo a=1 b="abc"} or nested metadata like {foo a=1 b="123" { bar x=12 text="hello" }}
66 *
67 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
68 * @version $Revision: 1.12 $
69 */
70 public class Node implements java.io.Serializable {
71
72 private static final long serialVersionUID = 4121134753270542643L;
73 private Node parent;
74 private Object name;
75 private Map attributes;
76 private Object value;
77
78 public Node(Node parent, Object name) {
79 this(parent, name, Collections.EMPTY_MAP, Collections.EMPTY_LIST);
80 }
81
82 public Node(Node parent, Object name, Object value) {
83 this(parent, name, Collections.EMPTY_MAP, value);
84 }
85
86 public Node(Node parent, Object name, Map attributes) {
87 this(parent, name, attributes, Collections.EMPTY_LIST);
88 }
89
90 public Node(Node parent, Object name, Map attributes, Object value) {
91 this.parent = parent;
92 this.name = name;
93 this.attributes = attributes;
94 this.value = value;
95
96 if (parent != null) {
97 Object parentValue = parent.value();
98 List parentList = null;
99 if (parentValue instanceof List) {
100 parentList = (List) parentValue;
101 }
102 else {
103 parentList = new NodeList();
104 parentList.add(parentValue);
105 parent.setValue(parentList);
106 }
107 parentList.add(this);
108 }
109 }
110
111 public String text() {
112 if (value instanceof String) {
113 return (String) value;
114 }
115 else if (value instanceof Collection) {
116 Collection coll = (Collection) value;
117 String previousText = null;
118 StringBuffer buffer = null;
119 for (Iterator iter = coll.iterator(); iter.hasNext();) {
120 Object child = iter.next();
121 if (child instanceof String) {
122 String childText = (String) child;
123 if (previousText == null) {
124 previousText = childText;
125 }
126 else {
127 if (buffer == null) {
128 buffer = new StringBuffer();
129 buffer.append(previousText);
130 }
131 buffer.append(childText);
132 }
133 }
134 }
135 if (buffer != null) {
136 return buffer.toString();
137 }
138 else {
139 if (previousText != null) {
140 return previousText;
141 }
142 }
143 }
144 return "";
145 }
146
147
148 public Iterator iterator() {
149 return children().iterator();
150 }
151
152 public List children() {
153 if (value == null) {
154 return Collections.EMPTY_LIST;
155 }
156 else if (value instanceof List) {
157 return (List) value;
158 }
159 else {
160
161 return Collections.singletonList(value);
162 }
163 }
164
165 public Map attributes() {
166 return attributes;
167 }
168
169 public Object attribute(Object key) {
170 return (attributes != null) ? attributes.get(key) : null;
171 }
172
173 public Object name() {
174 return name;
175 }
176
177 public Object value() {
178 return value;
179 }
180
181 public void setValue(Object value) {
182 this.value = value;
183 }
184
185 public Node parent() {
186 return parent;
187 }
188
189 /***
190 * Provides lookup of elements by non-namespaced name
191 */
192 public Object get(String key) {
193 if (key.charAt(0) == '@') {
194 String attributeName = key.substring(1);
195 return attributes().get(attributeName);
196 }
197 else {
198
199 List answer = new NodeList();
200 for (Iterator iter = children().iterator(); iter.hasNext();) {
201 Object child = iter.next();
202 if (child instanceof Node) {
203 Node childNode = (Node) child;
204 Object childNodeName = childNode.name();
205 if (childNodeName != null && childNodeName.equals(key)) {
206 answer.add(childNode);
207 }
208 }
209 }
210 return answer;
211 }
212 }
213
214 /***
215 * Provides lookup of elements by QName
216 */
217 public NodeList getAt(QName name) {
218 NodeList answer = new NodeList();
219 for (Iterator iter = children().iterator(); iter.hasNext();) {
220 Object child = iter.next();
221 if (child instanceof Node) {
222 Node childNode = (Node) child;
223 Object childNodeName = childNode.name();
224 if (childNodeName != null && childNodeName.equals(name)) {
225 answer.add(childNode);
226 }
227 }
228 }
229 return answer;
230 }
231
232
233
234
235
236
237
238 /***
239 * Provide a collection of all the nodes in the tree
240 * using a depth first traversal
241 */
242 public List depthFirst() {
243 List answer = new NodeList();
244 answer.add(this);
245 answer.addAll(depthFirstRest());
246 return answer;
247 }
248
249 private List depthFirstRest() {
250 List answer = new NodeList();
251 for (Iterator iter = InvokerHelper.asIterator(value); iter.hasNext(); ) {
252 Object child = iter.next();
253 if (child instanceof Node) {
254 Node childNode = (Node) child;
255 List children = childNode.depthFirstRest();
256 answer.add(childNode);
257 answer.addAll(children);
258 }
259 }
260 return answer;
261 }
262
263 /***
264 * Provide a collection of all the nodes in the tree
265 * using a bredth first traversal
266 */
267 public List breadthFirst() {
268 List answer = new NodeList();
269 answer.add(this);
270 answer.addAll(breadthFirstRest());
271 return answer;
272 }
273
274 private List breadthFirstRest() {
275 List answer = new NodeList();
276 for (Iterator iter = InvokerHelper.asIterator(value); iter.hasNext(); ) {
277 Object child = iter.next();
278 if (child instanceof Node) {
279 Node childNode = (Node) child;
280 answer.add(childNode);
281 }
282 }
283 List copy = new NodeList(answer);
284 for (Iterator iter = copy.iterator(); iter.hasNext(); ) {
285 Node childNode = (Node) iter.next();
286 List children = childNode.breadthFirstRest();
287 answer.addAll(children);
288 }
289 return answer;
290 }
291
292 public String toString() {
293 return name + "[attributes=" + attributes + "; value=" + value + "]";
294 }
295
296 public void print(PrintWriter out) {
297 new NodePrinter(out).print(this);
298 }
299 }