/*
 * 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.configuration2.tree;

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

An immutable default implementation for configuration nodes.

This class is used for an in-memory representation of hierarchical configuration data. It stores typical information like a node name, a value, child nodes, or attributes.

After their creation, instances cannot be manipulated. There are methods for updating properties, but these methods return new ImmutableNode instances. Instances are created using the nested Builder class.

Since:2.0
/** * <p> * An immutable default implementation for configuration nodes. * </p> * <p> * This class is used for an in-memory representation of hierarchical * configuration data. It stores typical information like a node name, a value, * child nodes, or attributes. * </p> * <p> * After their creation, instances cannot be manipulated. There are methods for * updating properties, but these methods return new {@code ImmutableNode} * instances. Instances are created using the nested {@code Builder} class. * </p> * * @since 2.0 */
public final class ImmutableNode {
The name of this node.
/** The name of this node. */
private final String nodeName;
The value of this node.
/** The value of this node. */
private final Object value;
A collection with the child nodes of this node.
/** A collection with the child nodes of this node. */
private final List<ImmutableNode> children;
A map with the attributes of this node.
/** A map with the attributes of this node. */
private final Map<String, Object> attributes;
Creates a new instance of ImmutableNode from the given Builder object.
Params:
  • b – the Builder
/** * Creates a new instance of {@code ImmutableNode} from the given * {@code Builder} object. * * @param b the {@code Builder} */
private ImmutableNode(final Builder b) { children = b.createChildren(); attributes = b.createAttributes(); nodeName = b.name; value = b.value; }
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 getNodeName() { return nodeName; }
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; }
Returns a list with the children of this node. This list cannot be modified.
Returns:a list with the child nodes
/** * Returns a list with the children of this node. This list cannot be * modified. * * @return a list with the child nodes */
public List<ImmutableNode> getChildren() { return children; }
Returns a list with the children of this node.
Params:
  • name – the node name to find
Returns:a list with the child nodes
/** * Returns a list with the children of this node. * * @param name the node name to find * * @return a list with the child nodes */
public List<ImmutableNode> getChildren(final String name) { final List<ImmutableNode> list = new ArrayList<>(); if (name == null) { return list; } for (final ImmutableNode node : children) { if (name.equals(node.getNodeName())) { list.add(node); } } return list; }
Returns a map with the attributes of this node. This map cannot be modified.
Returns:a map with this node's attributes
/** * Returns a map with the attributes of this node. This map cannot be * modified. * * @return a map with this node's attributes */
public Map<String, Object> getAttributes() { return attributes; }
Creates a new ImmutableNode instance which is a copy of this object with the name changed to the passed in value.
Params:
  • name – the name of the newly created node
Returns:the new node with the changed name
/** * Creates a new {@code ImmutableNode} instance which is a copy of this * object with the name changed to the passed in value. * * @param name the name of the newly created node * @return the new node with the changed name */
public ImmutableNode setName(final String name) { return new Builder(children, attributes).name(name).value(value) .create(); }
Creates a new ImmutableNode instance which is a copy of this object with the value changed to the passed in value.
Params:
  • newValue – the value of the newly created node
Returns:the new node with the changed value
/** * Creates a new {@code ImmutableNode} instance which is a copy of this * object with the value changed to the passed in value. * * @param newValue the value of the newly created node * @return the new node with the changed value */
public ImmutableNode setValue(final Object newValue) { return new Builder(children, attributes).name(nodeName).value(newValue) .create(); }
Creates a new ImmutableNode instance which is a copy of this object, but has the given child node added.
Params:
  • child – the child node to be added (must not be null)
Throws:
Returns:the new node with the child node added
/** * Creates a new {@code ImmutableNode} instance which is a copy of this * object, but has the given child node added. * * @param child the child node to be added (must not be <b>null</b>) * @return the new node with the child node added * @throws IllegalArgumentException if the child node is <b>null</b> */
public ImmutableNode addChild(final ImmutableNode child) { checkChildNode(child); final Builder builder = new Builder(children.size() + 1, attributes); builder.addChildren(children).addChild(child); return createWithBasicProperties(builder); }
Returns a new ImmutableNode instance which is a copy of this object, but with the given child node removed. If the child node does not belong to this node, the same node instance is returned.
Params:
  • child – the child node to be removed
Returns:the new node with the child node removed
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with the given child node removed. If the child node does not * belong to this node, the same node instance is returned. * * @param child the child node to be removed * @return the new node with the child node removed */
public ImmutableNode removeChild(final ImmutableNode child) { // use same size of children in case the child does not exist final Builder builder = new Builder(children.size(), attributes); boolean foundChild = false; for (final ImmutableNode c : children) { if (c == child) { foundChild = true; } else { builder.addChild(c); } } return foundChild ? createWithBasicProperties(builder) : this; }
Returns a new ImmutableNode instance which is a copy of this object, but with the given child replaced by the new one. If the child to be replaced cannot be found, the same node instance is returned.
Params:
  • oldChild – the child node to be replaced
  • newChild – the replacing child node (must not be null)
Throws:
Returns:the new node with the child replaced
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with the given child replaced by the new one. If the child to * be replaced cannot be found, the same node instance is returned. * * @param oldChild the child node to be replaced * @param newChild the replacing child node (must not be <b>null</b>) * @return the new node with the child replaced * @throws IllegalArgumentException if the new child node is <b>null</b> */
public ImmutableNode replaceChild(final ImmutableNode oldChild, final ImmutableNode newChild) { checkChildNode(newChild); final Builder builder = new Builder(children.size(), attributes); boolean foundChild = false; for (final ImmutableNode c : children) { if (c == oldChild) { builder.addChild(newChild); foundChild = true; } else { builder.addChild(c); } } return foundChild ? createWithBasicProperties(builder) : this; }
Returns a new ImmutableNode instance which is a copy of this object, but with the children replaced by the ones in the passed in collection. With this method all children can be replaced in a single step. For the collection the same rules apply as for Builder.addChildren(Collection<? extends ImmutableNode>).
Params:
  • newChildren – the collection with the new children (may be null)
Returns:the new node with replaced children
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with the children replaced by the ones in the passed in * collection. With this method all children can be replaced in a single * step. For the collection the same rules apply as for * {@link Builder#addChildren(Collection)}. * * @param newChildren the collection with the new children (may be * <b>null</b>) * @return the new node with replaced children */
public ImmutableNode replaceChildren(final Collection<ImmutableNode> newChildren) { final Builder builder = new Builder(null, attributes); builder.addChildren(newChildren); return createWithBasicProperties(builder); }
Returns a new ImmutableNode instance which is a copy of this object, but with the specified attribute set to the given value. If an attribute with this name does not exist, it is created now. Otherwise, the new value overrides the old one.
Params:
  • name – the name of the attribute
  • value – the attribute value
Returns:the new node with this attribute
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with the specified attribute set to the given value. If an * attribute with this name does not exist, it is created now. Otherwise, * the new value overrides the old one. * * @param name the name of the attribute * @param value the attribute value * @return the new node with this attribute */
public ImmutableNode setAttribute(final String name, final Object value) { final Map<String, Object> newAttrs = new HashMap<>(attributes); newAttrs.put(name, value); return createWithNewAttributes(newAttrs); }
Returns a new ImmutableNode instance which is a copy of this object, but with all attributes added defined by the given map. This method is analogous to setAttribute(String, Object), but all attributes in the given map are added. If the map is null or empty, this method has no effect.
Params:
  • newAttributes – the map with attributes to be added
Returns:the new node with these attributes
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with all attributes added defined by the given map. This * method is analogous to {@link #setAttribute(String, Object)}, but all * attributes in the given map are added. If the map is <b>null</b> or * empty, this method has no effect. * * @param newAttributes the map with attributes to be added * @return the new node with these attributes */
public ImmutableNode setAttributes(final Map<String, ?> newAttributes) { if (newAttributes == null || newAttributes.isEmpty()) { return this; } final Map<String, Object> newAttrs = new HashMap<>(attributes); newAttrs.putAll(newAttributes); return createWithNewAttributes(newAttrs); }
Returns a new ImmutableNode instance which is a copy of this object, but with the specified attribute removed. If there is no attribute with the given name, the same node instance is returned.
Params:
  • name – the name of the attribute
Returns:the new node without this attribute
/** * Returns a new {@code ImmutableNode} instance which is a copy of this * object, but with the specified attribute removed. If there is no * attribute with the given name, the same node instance is returned. * * @param name the name of the attribute * @return the new node without this attribute */
public ImmutableNode removeAttribute(final String name) { final Map<String, Object> newAttrs = new HashMap<>(attributes); if (newAttrs.remove(name) != null) { return createWithNewAttributes(newAttrs); } return this; }
Initializes the given builder with basic properties (node name and value) and returns the newly created node. This is a helper method for updating a node when only children or attributes are affected.
Params:
  • builder – the already prepared builder
Returns:the newly created node
/** * Initializes the given builder with basic properties (node name and value) * and returns the newly created node. This is a helper method for updating * a node when only children or attributes are affected. * * @param builder the already prepared builder * @return the newly created node */
private ImmutableNode createWithBasicProperties(final Builder builder) { return builder.name(nodeName).value(value).create(); }
Creates a new ImmutableNode instance with the same properties as this object, but with the given new attributes.
Params:
  • newAttrs – the new attributes
Returns:the new node instance
/** * Creates a new {@code ImmutableNode} instance with the same properties as * this object, but with the given new attributes. * * @param newAttrs the new attributes * @return the new node instance */
private ImmutableNode createWithNewAttributes(final Map<String, Object> newAttrs) { return createWithBasicProperties(new Builder(children, null) .addAttributes(newAttrs)); }
Checks whether the given child node is not null. This check is done at multiple places to ensure that newly added child nodes are always defined.
Params:
  • child – the child node to be checked
Throws:
/** * Checks whether the given child node is not null. This check is done at * multiple places to ensure that newly added child nodes are always * defined. * * @param child the child node to be checked * @throws IllegalArgumentException if the child node is <b>null</b> */
private static void checkChildNode(final ImmutableNode child) { if (child == null) { throw new IllegalArgumentException("Child node must not be null!"); } }

A builder class for creating instances of ImmutableNode.

This class can be used to set all properties of an immutable node instance. Eventually call the create() method to obtain the resulting instance.

Implementation note: This class is not thread-safe. It is intended to be used to define a single node instance only.

/** * <p> * A <em>builder</em> class for creating instances of {@code ImmutableNode}. * </p> * <p> * This class can be used to set all properties of an immutable node * instance. Eventually call the {@code create()} method to obtain the * resulting instance. * </p> * <p> * Implementation note: This class is not thread-safe. It is intended to be * used to define a single node instance only. * </p> */
public static final class Builder {
The direct list of children of the new node.
/** The direct list of children of the new node. */
private final List<ImmutableNode> directChildren;
The direct map of attributes of the new node.
/** The direct map of attributes of the new node. */
private final Map<String, Object> directAttributes;
A list for the children of the new node. This list is populated by the addChild() method.
/** * A list for the children of the new node. This list is populated by * the {@code addChild()} method. */
private List<ImmutableNode> children;
A map for storing the attributes of the new node. This map is populated by addAttribute().
/** * A map for storing the attributes of the new node. This map is * populated by {@code addAttribute()}. */
private Map<String, Object> attributes;
The name of the node.
/** The name of the node. */
private String name;
The value of the node.
/** The value of the node. */
private Object value;
Creates a new instance of Builder which does not contain any property definitions yet.
/** * Creates a new instance of {@code Builder} which does not contain any * property definitions yet. */
public Builder() { this(null, null); }
Creates a new instance of Builder and sets the number of expected child nodes. Using this constructor helps the class to create a properly sized list for the child nodes to be added.
Params:
  • childCount – the number of child nodes
/** * Creates a new instance of {@code Builder} and sets the number of * expected child nodes. Using this constructor helps the class to * create a properly sized list for the child nodes to be added. * * @param childCount the number of child nodes */
public Builder(final int childCount) { this(); initChildrenCollection(childCount); }
Creates a new instance of Builder and initializes the children and attributes of the new node. This constructor is used internally by the ImmutableNode class for creating instances derived from another node. The passed in collections are passed directly to the newly created instance; thus they already need to be immutable. (Background is that the creation of intermediate objects is to be avoided.)
Params:
  • dirChildren – the children of the new node
  • dirAttrs – the attributes of the new node
/** * Creates a new instance of {@code Builder} and initializes the * children and attributes of the new node. This constructor is used * internally by the {@code ImmutableNode} class for creating instances * derived from another node. The passed in collections are passed * directly to the newly created instance; thus they already need to be * immutable. (Background is that the creation of intermediate objects * is to be avoided.) * * @param dirChildren the children of the new node * @param dirAttrs the attributes of the new node */
private Builder(final List<ImmutableNode> dirChildren, final Map<String, Object> dirAttrs) { directChildren = dirChildren; directAttributes = dirAttrs; }
Creates a new instance of Builder and initializes the attributes of the new node and prepares the collection for the children. This constructor is used internally by methods of ImmutableNode which update the node and change the children. The new number of child nodes can be passed so that the collection for the new children can be created with an appropriate size.
Params:
  • childCount – the expected number of new children
  • dirAttrs – the attributes of the new node
/** * Creates a new instance of {@code Builder} and initializes the * attributes of the new node and prepares the collection for the * children. This constructor is used internally by methods of * {@code ImmutableNode} which update the node and change the children. * The new number of child nodes can be passed so that the collection * for the new children can be created with an appropriate size. * * @param childCount the expected number of new children * @param dirAttrs the attributes of the new node */
private Builder(final int childCount, final Map<String, Object> dirAttrs) { this(null, dirAttrs); initChildrenCollection(childCount); }
Sets the name of the node to be created.
Params:
  • n – the node name
Returns:a reference to this object for method chaining
/** * Sets the name of the node to be created. * * @param n the node name * @return a reference to this object for method chaining */
public Builder name(final String n) { name = n; return this; }
Sets the value of the node to be created.
Params:
  • v – the value
Returns:a reference to this object for method chaining
/** * Sets the value of the node to be created. * * @param v the value * @return a reference to this object for method chaining */
public Builder value(final Object v) { value = v; return this; }
Adds a child node to this builder. The passed in node becomes a child of the newly created node. If it is null, it is ignored.
Params:
  • c – the child node (must not be null)
Returns:a reference to this object for method chaining
/** * Adds a child node to this builder. The passed in node becomes a child * of the newly created node. If it is <b>null</b>, it is ignored. * * @param c the child node (must not be <b>null</b>) * @return a reference to this object for method chaining */
public Builder addChild(final ImmutableNode c) { if (c != null) { ensureChildrenExist(); children.add(c); } return this; }
Adds multiple child nodes to this builder. This method works like addChild(ImmutableNode), but it allows setting a number of child nodes at once.
Params:
  • children – a collection with the child nodes to be added
Returns:a reference to this object for method chaining
/** * Adds multiple child nodes to this builder. This method works like * {@link #addChild(ImmutableNode)}, but it allows setting a number of * child nodes at once. * * * @param children a collection with the child nodes to be added * @return a reference to this object for method chaining */
public Builder addChildren(final Collection<? extends ImmutableNode> children) { if (children != null) { ensureChildrenExist(); this.children.addAll(filterNull(children)); } return this; }
Adds an attribute to this builder. The passed in attribute key and value are stored in an internal map. If there is already an attribute with this name, it is overridden.
Params:
  • name – the attribute name
  • value – the attribute value
Returns:a reference to this object for method chaining
/** * Adds an attribute to this builder. The passed in attribute key and * value are stored in an internal map. If there is already an attribute * with this name, it is overridden. * * @param name the attribute name * @param value the attribute value * @return a reference to this object for method chaining */
public Builder addAttribute(final String name, final Object value) { ensureAttributesExist(); attributes.put(name, value); return this; }
Adds all attributes of the given map to this builder. This method works like addAttribute(String, Object), but it allows setting multiple attributes at once.
Params:
  • attrs – the map with attributes to be added (may be null
Returns:a reference to this object for method chaining
/** * Adds all attributes of the given map to this builder. This method * works like {@link #addAttribute(String, Object)}, but it allows * setting multiple attributes at once. * * @param attrs the map with attributes to be added (may be <b>null</b> * @return a reference to this object for method chaining */
public Builder addAttributes(final Map<String, ?> attrs) { if (attrs != null) { ensureAttributesExist(); attributes.putAll(attrs); } return this; }
Creates a new ImmutableNode instance based on the properties set for this builder.
Returns:the newly created ImmutableNode
/** * Creates a new {@code ImmutableNode} instance based on the properties * set for this builder. * * @return the newly created {@code ImmutableNode} */
public ImmutableNode create() { final ImmutableNode newNode = new ImmutableNode(this); children = null; attributes = null; return newNode; }
Creates a list with the children of the newly created node. The list returned here is always immutable. It depends on the way this builder was populated.
Returns:the list with the children of the new node
/** * Creates a list with the children of the newly created node. The list * returned here is always immutable. It depends on the way this builder * was populated. * * @return the list with the children of the new node */
List<ImmutableNode> createChildren() { if (directChildren != null) { return directChildren; } if (children != null) { return Collections.unmodifiableList(children); } return Collections.emptyList(); }
Creates a map with the attributes of the newly created node. This is an immutable map. If direct attributes were set, they are returned. Otherwise an unmodifiable map from the attributes passed to this builder is constructed.
Returns:a map with the attributes for the new node
/** * Creates a map with the attributes of the newly created node. This is * an immutable map. If direct attributes were set, they are returned. * Otherwise an unmodifiable map from the attributes passed to this * builder is constructed. * * @return a map with the attributes for the new node */
private Map<String, Object> createAttributes() { if (directAttributes != null) { return directAttributes; } if (attributes != null) { return Collections.unmodifiableMap(attributes); } return Collections.emptyMap(); }
Ensures that the collection for the child nodes exists. It is created on demand.
/** * Ensures that the collection for the child nodes exists. It is created * on demand. */
private void ensureChildrenExist() { if (children == null) { children = new LinkedList<>(); } }
Ensures that the map for the attributes exists. It is created on demand.
/** * Ensures that the map for the attributes exists. It is created on * demand. */
private void ensureAttributesExist() { if (attributes == null) { attributes = new HashMap<>(); } }
Creates the collection for child nodes based on the expected number of children.
Params:
  • childCount – the expected number of new children
/** * Creates the collection for child nodes based on the expected number * of children. * * @param childCount the expected number of new children */
private void initChildrenCollection(final int childCount) { if (childCount > 0) { children = new ArrayList<>(childCount); } }
Filters null entries from the passed in collection with child nodes.
Params:
  • children – the collection to be filtered
Returns:the collection with null entries removed
/** * Filters null entries from the passed in collection with child nodes. * * * @param children the collection to be filtered * @return the collection with null entries removed */
private static Collection<? extends ImmutableNode> filterNull( final Collection<? extends ImmutableNode> children) { final List<ImmutableNode> result = new ArrayList<>(children.size()); for (final ImmutableNode c : children) { if (c != null) { result.add(c); } } return result; } } @Override public String toString() { return super.toString() + "(" + nodeName + ")"; } }