/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
/**
* Simplified implementation of a Node from a Document Object Model (DOM)
* parse of an XML document. This class is used to represent a DOM tree
* so that the XML parser's implementation of <code>org.w3c.dom</code> need
* not be visible to the remainder of Jasper.
* <p>
* <strong>WARNING</strong> - Construction of a new tree, or modifications
* to an existing one, are not thread-safe and such accesses must be
* synchronized.
*
* @author Craig R. McClanahan
* @version $Revision: 515 $ $Date: 2008-03-17 22:02:23 +0100 (Mon, 17 Mar 2008) $
*/
public class TreeNode {
// ----------------------------------------------------------- Constructors
/**
* Construct a new node with no parent.
*
* @param name The name of this node
*/
public TreeNode(String name) {
this(name, null);
}
/**
* Construct a new node with the specified parent.
*
* @param name The name of this node
* @param parent The node that is the parent of this node
*/
public TreeNode(String name, TreeNode parent) {
super();
this.name = name;
this.parent = parent;
if (this.parent != null)
this.parent.addChild(this);
}
// ----------------------------------------------------- Instance Variables
/**
* The attributes of this node, keyed by attribute name,
* Instantiated only if required.
*/
protected HashMap attributes = null;
/**
* The body text associated with this node (if any).
*/
protected String body = null;
/**
* The children of this node, instantiated only if required.
*/
protected ArrayList children = null;
/**
* The name of this node.
*/
protected String name = null;
/**
* The parent node of this node.
*/
protected TreeNode parent = null;
// --------------------------------------------------------- Public Methods
/**
* Add an attribute to this node, replacing any existing attribute
* with the same name.
*
* @param name The attribute name to add
* @param value The new attribute value
*/
public void addAttribute(String name, String value) {
if (attributes == null)
attributes = new HashMap();
attributes.put(name, value);
}
/**
* Add a new child node to this node.
*
* @param node The new child node
*/
public void addChild(TreeNode node) {
if (children == null)
children = new ArrayList();
children.add(node);
}
/**
* Return the value of the specified node attribute if it exists, or
* <code>null</code> otherwise.
*
* @param name Name of the requested attribute
*/
public String findAttribute(String name) {
if (attributes == null)
return (null);
else
return ((String) attributes.get(name));
}
/**
* Return an Iterator of the attribute names of this node. If there are
* no attributes, an empty Iterator is returned.
*/
public Iterator findAttributes() {
if (attributes == null)
return (Collections.EMPTY_LIST.iterator());
else
return (attributes.keySet().iterator());
}
/**
* Return the first child node of this node with the specified name,
* if there is one; otherwise, return <code>null</code>.
*
* @param name Name of the desired child element
*/
public TreeNode findChild(String name) {
if (children == null)
return (null);
Iterator items = children.iterator();
while (items.hasNext()) {
TreeNode item = (TreeNode) items.next();
if (name.equals(item.getName()))
return (item);
}
return (null);
}
/**
* Return an Iterator of all children of this node. If there are no
* children, an empty Iterator is returned.
*/
public Iterator findChildren() {
if (children == null)
return (Collections.EMPTY_LIST.iterator());
else
return (children.iterator());
}
/**
* Return an Iterator over all children of this node that have the
* specified name. If there are no such children, an empty Iterator
* is returned.
*
* @param name Name used to select children
*/
public Iterator findChildren(String name) {
if (children == null)
return (Collections.EMPTY_LIST.iterator());
ArrayList results = new ArrayList();
Iterator items = children.iterator();
while (items.hasNext()) {
TreeNode item = (TreeNode) items.next();
if (name.equals(item.getName()))
results.add(item);
}
return (results.iterator());
}
/**
* Return the body text associated with this node (if any).
*/
public String getBody() {
return (this.body);
}
/**
* Return the name of this node.
*/
public String getName() {
return (this.name);
}
/**
* Remove any existing value for the specified attribute name.
*
* @param name The attribute name to remove
*/
public void removeAttribute(String name) {
if (attributes != null)
attributes.remove(name);
}
/**
* Remove a child node from this node, if it is one.
*
* @param node The child node to remove
*/
public void removeNode(TreeNode node) {
if (children != null)
children.remove(node);
}
/**
* Set the body text associated with this node (if any).
*
* @param body The body text (if any)
*/
public void setBody(String body) {
this.body = body;
}
/**
* Return a String representation of this TreeNode.
*/
public String toString() {
StringBuffer sb = new StringBuffer();
toString(sb, 0, this);
return (sb.toString());
}
// ------------------------------------------------------ Protected Methods
/**
* Append to the specified StringBuffer a character representation of
* this node, with the specified amount of indentation.
*
* @param sb The StringBuffer to append to
* @param indent Number of characters of indentation
* @param node The TreeNode to be printed
*/
protected void toString(StringBuffer sb, int indent,
TreeNode node) {
int indent2 = indent + 2;
// Reconstruct an opening node
for (int i = 0; i < indent; i++)
sb.append(' ');
sb.append('<');
sb.append(node.getName());
Iterator names = node.findAttributes();
while (names.hasNext()) {
sb.append(' ');
String name = (String) names.next();
sb.append(name);
sb.append("=\"");
String value = node.findAttribute(name);
sb.append(value);
sb.append("\"");
}
sb.append(">\n");
// Reconstruct the body text of this node (if any)
String body = node.getBody();
if ((body != null) && (body.length() > 0)) {
for (int i = 0; i < indent2; i++)
sb.append(' ');
sb.append(body);
sb.append("\n");
}
// Reconstruct child nodes with extra indentation
Iterator children = node.findChildren();
while (children.hasNext()) {
TreeNode child = (TreeNode) children.next();
toString(sb, indent2, child);
}
// Reconstruct a closing node marker
for (int i = 0; i < indent; i++)
sb.append(' ');
sb.append("</");
sb.append(node.getName());
sb.append(">\n");
}
}