/*
 * 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.
 */
package org.apache.commons.configuration.tree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.configuration.ConfigurationRuntimeException;

A default implementation of the ConfigurationNode interface.

Author:Commons Configuration team
Since:1.3
Version:$Id: DefaultConfigurationNode.java 1301991 2012-03-17 20:18:02Z sebb $
/** * <p> * A default implementation of the {@code ConfigurationNode} interface. * </p> * * @since 1.3 * @author <a * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team</a> * @version $Id: DefaultConfigurationNode.java 1301991 2012-03-17 20:18:02Z sebb $ */
public class DefaultConfigurationNode implements ConfigurationNode, Cloneable {
Stores the children of this node.
/** Stores the children of this node. */
private SubNodes children;
Stores the attributes of this node.
/** Stores the attributes of this node. */
private SubNodes attributes;
Stores a reference to this node's parent.
/** Stores a reference to this node's parent. */
private ConfigurationNode parent;
Stores the value of this node.
/** Stores the value of this node. */
private Object value;
Stores the reference.
/** Stores the reference. */
private Object reference;
Stores the name of this node.
/** Stores the name of this node. */
private String name;
Stores a flag if this is an attribute.
/** Stores a flag if this is an attribute. */
private boolean attribute;
Creates a new uninitialized instance of DefaultConfigurationNode.
/** * Creates a new uninitialized instance of {@code DefaultConfigurationNode}. */
public DefaultConfigurationNode() { this(null); }
Creates a new instance of DefaultConfigurationNode and initializes it with the node name.
Params:
  • name – the name of this node
/** * Creates a new instance of {@code DefaultConfigurationNode} and * initializes it with the node name. * * @param name the name of this node */
public DefaultConfigurationNode(String name) { this(name, null); }
Creates a new instance of DefaultConfigurationNode and initializes it with the name and a value.
Params:
  • name – the node's name
  • value – the node's value
/** * Creates a new instance of {@code DefaultConfigurationNode} and * initializes it with the name and a value. * * @param name the node's name * @param value the node's value */
public DefaultConfigurationNode(String name, Object value) { setName(name); setValue(value); initSubNodes(); }
Returns the name of this node.
Returns:the name of this node
/** * Returns the name of this node. * * @return the name of this node */
public String getName() { return name; }
Sets the name of this node.
Params:
  • name – the new name
/** * Sets the name of this node. * * @param name the new name */
public void setName(String name) { checkState(); this.name = name; }
Returns the value of this node.
Returns:the value of this node
/** * Returns the value of this node. * * @return the value of this node */
public Object getValue() { return value; }
Sets the value of this node.
Params:
  • val – the value of this node
/** * Sets the value of this node. * * @param val the value of this node */
public void setValue(Object val) { value = val; }
Returns the reference.
Returns:the reference
/** * Returns the reference. * * @return the reference */
public Object getReference() { return reference; }
Sets the reference.
Params:
  • reference – the reference object
/** * Sets the reference. * * @param reference the reference object */
public void setReference(Object reference) { this.reference = reference; }
Returns a reference to this node's parent.
Returns:the parent node or null if this is the root
/** * Returns a reference to this node's parent. * * @return the parent node or <b>null </b> if this is the root */
public ConfigurationNode getParentNode() { return parent; }
Sets the parent of this node.
Params:
  • parent – the parent of this node
/** * Sets the parent of this node. * * @param parent the parent of this node */
public void setParentNode(ConfigurationNode parent) { this.parent = parent; }
Adds a new child to this node.
Params:
  • child – the new child
/** * Adds a new child to this node. * * @param child the new child */
public void addChild(ConfigurationNode child) { children.addNode(child); child.setAttribute(false); child.setParentNode(this); }
Returns a list with all children of this node.
Returns:a list with all child nodes
/** * Returns a list with all children of this node. * * @return a list with all child nodes */
public List<ConfigurationNode> getChildren() { return children.getSubNodes(); }
Returns the number of all children of this node.
Returns:the number of all children
/** * Returns the number of all children of this node. * * @return the number of all children */
public int getChildrenCount() { return children.getSubNodes().size(); }
Returns a list of all children with the given name.
Params:
  • name – the name; can be null , then all children are returned
Returns:a list of all children with the given name
/** * Returns a list of all children with the given name. * * @param name the name; can be <b>null </b>, then all children are returned * @return a list of all children with the given name */
public List<ConfigurationNode> getChildren(String name) { return children.getSubNodes(name); }
Returns the number of children with the given name.
Params:
  • name – the name; can be null , then the number of all children is returned
Returns:the number of child nodes with this name
/** * Returns the number of children with the given name. * * @param name the name; can be <b>null </b>, then the number of all * children is returned * @return the number of child nodes with this name */
public int getChildrenCount(String name) { return children.getSubNodes(name).size(); }
Returns the child node with the given index.
Params:
  • index – the index (0-based)
Returns:the child with this index
/** * Returns the child node with the given index. * * @param index the index (0-based) * @return the child with this index */
public ConfigurationNode getChild(int index) { return children.getNode(index); }
Removes the specified child node from this node.
Params:
  • child – the node to be removed
Returns:a flag if a node was removed
/** * Removes the specified child node from this node. * * @param child the node to be removed * @return a flag if a node was removed */
public boolean removeChild(ConfigurationNode child) { return children.removeNode(child); }
Removes all children with the given name.
Params:
  • childName – the name of the children to be removed
Returns:a flag if at least one child node was removed
/** * Removes all children with the given name. * * @param childName the name of the children to be removed * @return a flag if at least one child node was removed */
public boolean removeChild(String childName) { return children.removeNodes(childName); }
Removes all child nodes of this node.
/** * Removes all child nodes of this node. */
public void removeChildren() { children.clear(); }
Checks if this node is an attribute node.
Returns:a flag if this is an attribute node
/** * Checks if this node is an attribute node. * * @return a flag if this is an attribute node */
public boolean isAttribute() { return attribute; }
Sets the attribute flag. Note: this method can only be called if the node is not already part of a node hierarchy.
Params:
  • f – the attribute flag
/** * Sets the attribute flag. Note: this method can only be called if the node * is not already part of a node hierarchy. * * @param f the attribute flag */
public void setAttribute(boolean f) { checkState(); attribute = f; }
Adds the specified attribute to this node.
Params:
  • attr – the attribute to be added
/** * Adds the specified attribute to this node. * * @param attr the attribute to be added */
public void addAttribute(ConfigurationNode attr) { attributes.addNode(attr); attr.setAttribute(true); attr.setParentNode(this); }
Returns a list with the attributes of this node. This list contains DefaultConfigurationNode objects, too.
Returns:the attribute list, never null
/** * Returns a list with the attributes of this node. This list contains * {@code DefaultConfigurationNode} objects, too. * * @return the attribute list, never <b>null </b> */
public List<ConfigurationNode> getAttributes() { return attributes.getSubNodes(); }
Returns the number of attributes contained in this node.
Returns:the number of attributes
/** * Returns the number of attributes contained in this node. * * @return the number of attributes */
public int getAttributeCount() { return attributes.getSubNodes().size(); }
Returns a list with all attributes of this node with the given name.
Params:
  • name – the attribute's name
Returns:all attributes with this name
/** * Returns a list with all attributes of this node with the given name. * * @param name the attribute's name * @return all attributes with this name */
public List<ConfigurationNode> getAttributes(String name) { return attributes.getSubNodes(name); }
Returns the number of attributes of this node with the given name.
Params:
  • name – the name
Returns:the number of attributes with this name
/** * Returns the number of attributes of this node with the given name. * * @param name the name * @return the number of attributes with this name */
public int getAttributeCount(String name) { return getAttributes(name).size(); }
Removes the specified attribute.
Params:
  • node – the attribute node to be removed
Returns:a flag if the attribute could be removed
/** * Removes the specified attribute. * * @param node the attribute node to be removed * @return a flag if the attribute could be removed */
public boolean removeAttribute(ConfigurationNode node) { return attributes.removeNode(node); }
Removes all attributes with the specified name.
Params:
  • name – the name
Returns:a flag if at least one attribute was removed
/** * Removes all attributes with the specified name. * * @param name the name * @return a flag if at least one attribute was removed */
public boolean removeAttribute(String name) { return attributes.removeNodes(name); }
Returns the attribute with the given index.
Params:
  • index – the index (0-based)
Returns:the attribute with this index
/** * Returns the attribute with the given index. * * @param index the index (0-based) * @return the attribute with this index */
public ConfigurationNode getAttribute(int index) { return attributes.getNode(index); }
Removes all attributes of this node.
/** * Removes all attributes of this node. */
public void removeAttributes() { attributes.clear(); }
Returns a flag if this node is defined. This means that the node contains some data.
Returns:a flag whether this node is defined
/** * Returns a flag if this node is defined. This means that the node contains * some data. * * @return a flag whether this node is defined */
public boolean isDefined() { return getValue() != null || getChildrenCount() > 0 || getAttributeCount() > 0; }
Visits this node and all its sub nodes.
Params:
  • visitor – the visitor
/** * Visits this node and all its sub nodes. * * @param visitor the visitor */
public void visit(ConfigurationNodeVisitor visitor) { if (visitor == null) { throw new IllegalArgumentException("Visitor must not be null!"); } if (!visitor.terminate()) { visitor.visitBeforeChildren(this); children.visit(visitor); attributes.visit(visitor); visitor.visitAfterChildren(this); } }
Creates a copy of this object. This is not a deep copy, the children are not cloned.
Returns:a copy of this object
/** * Creates a copy of this object. This is not a deep copy, the children are * not cloned. * * @return a copy of this object */
@Override public Object clone() { try { DefaultConfigurationNode copy = (DefaultConfigurationNode) super .clone(); copy.initSubNodes(); return copy; } catch (CloneNotSupportedException cex) { // should not happen throw new ConfigurationRuntimeException("Cannot clone " + getClass()); } }
Checks if a modification of this node is allowed. Some properties of a node must not be changed when the node has a parent. This method checks this and throws a runtime exception if necessary.
/** * Checks if a modification of this node is allowed. Some properties of a * node must not be changed when the node has a parent. This method checks * this and throws a runtime exception if necessary. */
protected void checkState() { if (getParentNode() != null) { throw new IllegalStateException( "Node cannot be modified when added to a parent!"); } }
Creates a SubNodes instance that is used for storing either this node's children or attributes.
Params:
  • attributes – true if the returned instance is used for storing attributes, false for storing child nodes
Returns:the SubNodes object to use
/** * Creates a {@code SubNodes} instance that is used for storing * either this node's children or attributes. * * @param attributes <b>true</b> if the returned instance is used for * storing attributes, <b>false</b> for storing child nodes * @return the {@code SubNodes} object to use */
protected SubNodes createSubNodes(boolean attributes) { return new SubNodes(); }
Deals with the reference when a node is removed. This method is called for each removed child node or attribute. It can be overloaded in sub classes, for which the reference has a concrete meaning and remove operations need some update actions. This default implementation is empty.
/** * Deals with the reference when a node is removed. This method is called * for each removed child node or attribute. It can be overloaded in sub * classes, for which the reference has a concrete meaning and remove * operations need some update actions. This default implementation is * empty. */
protected void removeReference() { }
Helper method for initializing the sub nodes objects.
/** * Helper method for initializing the sub nodes objects. */
private void initSubNodes() { children = createSubNodes(false); attributes = createSubNodes(true); }
An internally used helper class for managing a collection of sub nodes.
/** * An internally used helper class for managing a collection of sub nodes. */
protected static class SubNodes {
Stores a list for the sub nodes.
/** Stores a list for the sub nodes. */
private List<ConfigurationNode> nodes;
Stores a map for accessing subnodes by name.
/** Stores a map for accessing subnodes by name. */
private Map<String, List<ConfigurationNode>> namedNodes;
Adds a new sub node.
Params:
  • node – the node to add
/** * Adds a new sub node. * * @param node the node to add */
public void addNode(ConfigurationNode node) { if (node == null || node.getName() == null) { throw new IllegalArgumentException( "Node to add must have a defined name!"); } node.setParentNode(null); // reset, will later be set if (nodes == null) { nodes = new ArrayList<ConfigurationNode>(); namedNodes = new HashMap<String, List<ConfigurationNode>>(); } nodes.add(node); List<ConfigurationNode> lst = namedNodes.get(node.getName()); if (lst == null) { lst = new LinkedList<ConfigurationNode>(); namedNodes.put(node.getName(), lst); } lst.add(node); }
Removes a sub node.
Params:
  • node – the node to remove
Returns:a flag if the node could be removed
/** * Removes a sub node. * * @param node the node to remove * @return a flag if the node could be removed */
public boolean removeNode(ConfigurationNode node) { if (nodes != null && node != null && nodes.contains(node)) { detachNode(node); nodes.remove(node); List<ConfigurationNode> lst = namedNodes.get(node.getName()); if (lst != null) { lst.remove(node); if (lst.isEmpty()) { namedNodes.remove(node.getName()); } } return true; } else { return false; } }
Removes all sub nodes with the given name.
Params:
  • name – the name
Returns:a flag if at least on sub node was removed
/** * Removes all sub nodes with the given name. * * @param name the name * @return a flag if at least on sub node was removed */
public boolean removeNodes(String name) { if (nodes != null && name != null) { List<ConfigurationNode> lst = namedNodes.remove(name); if (lst != null) { detachNodes(lst); nodes.removeAll(lst); return true; } } return false; }
Removes all sub nodes.
/** * Removes all sub nodes. */
public void clear() { if (nodes != null) { detachNodes(nodes); nodes = null; namedNodes = null; } }
Returns the node with the given index. If this index cannot be found, an IndexOutOfBoundException exception will be thrown.
Params:
  • index – the index (0-based)
Returns:the sub node at the specified index
/** * Returns the node with the given index. If this index cannot be found, * an {@code IndexOutOfBoundException} exception will be thrown. * * @param index the index (0-based) * @return the sub node at the specified index */
public ConfigurationNode getNode(int index) { if (nodes == null) { throw new IndexOutOfBoundsException("No sub nodes available!"); } return nodes.get(index); }
Returns a list with all stored sub nodes. The return value is never null.
Returns:a list with the sub nodes
/** * Returns a list with all stored sub nodes. The return value is never * <b>null</b>. * * @return a list with the sub nodes */
public List<ConfigurationNode> getSubNodes() { if (nodes == null) { return Collections.emptyList(); } else { return Collections.unmodifiableList(nodes); } }
Returns a list of the sub nodes with the given name. The return value is never null.
Params:
  • name – the name; if null is passed, all sub nodes will be returned
Returns:all sub nodes with this name
/** * Returns a list of the sub nodes with the given name. The return value * is never <b>null</b>. * * @param name the name; if <b>null</b> is passed, all sub nodes will * be returned * @return all sub nodes with this name */
public List<ConfigurationNode> getSubNodes(String name) { if (name == null) { return getSubNodes(); } List<ConfigurationNode> result; if (nodes == null) { result = null; } else { result = namedNodes.get(name); } if (result == null) { return Collections.emptyList(); } else { return Collections.unmodifiableList(result); } }
Let the passed in visitor visit all sub nodes.
Params:
  • visitor – the visitor
/** * Let the passed in visitor visit all sub nodes. * * @param visitor the visitor */
public void visit(ConfigurationNodeVisitor visitor) { if (nodes != null) { for (Iterator<ConfigurationNode> it = nodes.iterator(); it.hasNext() && !visitor.terminate();) { it.next().visit(visitor); } } }
This method is called whenever a sub node is removed from this object. It ensures that the removed node's parent is reset and its removeReference() method gets called.
Params:
  • subNode – the node to be removed
/** * This method is called whenever a sub node is removed from this * object. It ensures that the removed node's parent is reset and its * {@code removeReference()} method gets called. * * @param subNode the node to be removed */
protected void detachNode(ConfigurationNode subNode) { subNode.setParentNode(null); if (subNode instanceof DefaultConfigurationNode) { ((DefaultConfigurationNode) subNode).removeReference(); } }
Detaches a list of sub nodes. This method calls detachNode() for each node contained in the list.
Params:
  • subNodes – the list with nodes to be detached
/** * Detaches a list of sub nodes. This method calls * {@code detachNode()} for each node contained in the list. * * @param subNodes the list with nodes to be detached */
protected void detachNodes(Collection<? extends ConfigurationNode> subNodes) { for (ConfigurationNode nd : subNodes) { detachNode(nd); } } } }