001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package com.icesoft.jasper.xmlparser;
018:
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Iterator;
023:
024: /**
025: * Simplified implementation of a Node from a Document Object Model (DOM) parse
026: * of an XML document. This class is used to represent a DOM tree so that the
027: * XML parser's implementation of <code>org.w3c.dom</code> need not be visible
028: * to the remainder of Jasper.
029: * <p/>
030: * <strong>WARNING</strong> - Construction of a new tree, or modifications to an
031: * existing one, are not thread-safe and such accesses must be synchronized.
032: *
033: * @author Craig R. McClanahan
034: * @version $Revision: 1.2 $ $Date: 2004/03/17 19:23:05 $
035: */
036:
037: public class TreeNode {
038:
039: // ----------------------------------------------------------- Constructors
040:
041: /**
042: * Construct a new node with no parent.
043: *
044: * @param name The name of this node
045: */
046: public TreeNode(String name) {
047:
048: this (name, null);
049:
050: }
051:
052: /**
053: * Construct a new node with the specified parent.
054: *
055: * @param name The name of this node
056: * @param parent The node that is the parent of this node
057: */
058: public TreeNode(String name, TreeNode parent) {
059:
060: super ();
061: this .name = name;
062: this .parent = parent;
063: if (this .parent != null)
064: this .parent.addChild(this );
065:
066: }
067:
068: // ----------------------------------------------------- Instance Variables
069:
070: /**
071: * The attributes of this node, keyed by attribute name, Instantiated only
072: * if required.
073: */
074: protected HashMap attributes = null;
075:
076: /**
077: * The body text associated with this node (if any).
078: */
079: protected String body = null;
080:
081: /**
082: * The children of this node, instantiated only if required.
083: */
084: protected ArrayList children = null;
085:
086: /**
087: * The name of this node.
088: */
089: protected String name = null;
090:
091: /**
092: * The parent node of this node.
093: */
094: protected TreeNode parent = null;
095:
096: // --------------------------------------------------------- Public Methods
097:
098: /**
099: * Add an attribute to this node, replacing any existing attribute with the
100: * same name.
101: *
102: * @param name The attribute name to add
103: * @param value The new attribute value
104: */
105: public void addAttribute(String name, String value) {
106:
107: if (attributes == null)
108: attributes = new HashMap();
109: attributes.put(name, value);
110:
111: }
112:
113: /**
114: * Add a new child node to this node.
115: *
116: * @param node The new child node
117: */
118: public void addChild(TreeNode node) {
119:
120: if (children == null)
121: children = new ArrayList();
122: children.add(node);
123:
124: }
125:
126: /**
127: * Return the value of the specified node attribute if it exists, or
128: * <code>null</code> otherwise.
129: *
130: * @param name Name of the requested attribute
131: */
132: public String findAttribute(String name) {
133:
134: if (attributes == null)
135: return (null);
136: else
137: return ((String) attributes.get(name));
138:
139: }
140:
141: /**
142: * Return an Iterator of the attribute names of this node. If there are no
143: * attributes, an empty Iterator is returned.
144: */
145: public Iterator findAttributes() {
146:
147: if (attributes == null)
148: return (Collections.EMPTY_LIST.iterator());
149: else
150: return (attributes.keySet().iterator());
151:
152: }
153:
154: /**
155: * Return the first child node of this node with the specified name, if
156: * there is one; otherwise, return <code>null</code>.
157: *
158: * @param name Name of the desired child element
159: */
160: public TreeNode findChild(String name) {
161:
162: if (children == null)
163: return (null);
164: Iterator items = children.iterator();
165: while (items.hasNext()) {
166: TreeNode item = (TreeNode) items.next();
167: if (name.equals(item.getName()))
168: return (item);
169: }
170: return (null);
171:
172: }
173:
174: /**
175: * Return an Iterator of all children of this node. If there are no
176: * children, an empty Iterator is returned.
177: */
178: public Iterator findChildren() {
179:
180: if (children == null)
181: return (Collections.EMPTY_LIST.iterator());
182: else
183: return (children.iterator());
184:
185: }
186:
187: /**
188: * Return an Iterator over all children of this node that have the specified
189: * name. If there are no such children, an empty Iterator is returned.
190: *
191: * @param name Name used to select children
192: */
193: public Iterator findChildren(String name) {
194:
195: if (children == null)
196: return (Collections.EMPTY_LIST.iterator());
197:
198: ArrayList results = new ArrayList();
199: Iterator items = children.iterator();
200: while (items.hasNext()) {
201: TreeNode item = (TreeNode) items.next();
202: if (name.equals(item.getName()))
203: results.add(item);
204: }
205: return (results.iterator());
206:
207: }
208:
209: /**
210: * Return the body text associated with this node (if any).
211: */
212: public String getBody() {
213:
214: return (this .body);
215:
216: }
217:
218: /**
219: * Return the name of this node.
220: */
221: public String getName() {
222:
223: return (this .name);
224:
225: }
226:
227: /**
228: * Remove any existing value for the specified attribute name.
229: *
230: * @param name The attribute name to remove
231: */
232: public void removeAttribute(String name) {
233:
234: if (attributes != null)
235: attributes.remove(name);
236:
237: }
238:
239: /**
240: * Remove a child node from this node, if it is one.
241: *
242: * @param node The child node to remove
243: */
244: public void removeNode(TreeNode node) {
245:
246: if (children != null)
247: children.remove(node);
248:
249: }
250:
251: /**
252: * Set the body text associated with this node (if any).
253: *
254: * @param body The body text (if any)
255: */
256: public void setBody(String body) {
257:
258: this .body = body;
259:
260: }
261:
262: /**
263: * Return a String representation of this TreeNode.
264: */
265: public String toString() {
266:
267: StringBuffer sb = new StringBuffer();
268: toString(sb, 0, this );
269: return (sb.toString());
270:
271: }
272:
273: // ------------------------------------------------------ Protected Methods
274:
275: /**
276: * Append to the specified StringBuffer a character representation of this
277: * node, with the specified amount of indentation.
278: *
279: * @param sb The StringBuffer to append to
280: * @param indent Number of characters of indentation
281: * @param node The TreeNode to be printed
282: */
283: protected void toString(StringBuffer sb, int indent, TreeNode node) {
284:
285: int indent2 = indent + 2;
286:
287: // Reconstruct an opening node
288: for (int i = 0; i < indent; i++)
289: sb.append(' ');
290: sb.append('<');
291: sb.append(node.getName());
292: Iterator names = node.findAttributes();
293: while (names.hasNext()) {
294: sb.append(' ');
295: String name = (String) names.next();
296: sb.append(name);
297: sb.append("=\"");
298: String value = node.findAttribute(name);
299: sb.append(value);
300: sb.append("\"");
301: }
302: sb.append(">\n");
303:
304: // Reconstruct the body text of this node (if any)
305: String body = node.getBody();
306: if ((body != null) && (body.length() > 0)) {
307: for (int i = 0; i < indent2; i++)
308: sb.append(' ');
309: sb.append(body);
310: sb.append("\n");
311: }
312:
313: // Reconstruct child nodes with extra indentation
314: Iterator children = node.findChildren();
315: while (children.hasNext()) {
316: TreeNode child = (TreeNode) children.next();
317: toString(sb, indent2, child);
318: }
319:
320: // Reconstruct a closing node marker
321: for (int i = 0; i < indent; i++)
322: sb.append(' ');
323: sb.append("</");
324: sb.append(node.getName());
325: sb.append(">\n");
326:
327: }
328:
329: }
|