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