You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2014/04/05 21:42:49 UTC
svn commit: r1585178 - in
/commons/proper/configuration/branches/immutableNodes/src:
main/java/org/apache/commons/configuration/tree/
test/java/org/apache/commons/configuration/tree/
Author: oheger
Date: Sat Apr 5 19:42:49 2014
New Revision: 1585178
URL: http://svn.apache.org/r1585178
Log:
Fixed some problems introduced in the latest merge operation.
Some versions had been overridden and had to be restored.
Added:
commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/QueryResult.java
Modified:
commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java
commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ExpressionEngine.java
commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeAddData.java
commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeHandler.java
commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestDefaultExpressionEngine.java
Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java?rev=1585178&r1=1585177&r2=1585178&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/DefaultExpressionEngine.java Sat Apr 5 19:42:49 2014
@@ -158,35 +158,26 @@ public class DefaultExpressionEngine imp
}
/**
- * Evaluates the given key and returns all matching nodes. This method
- * supports the syntax as described in the class comment.
- *
- * @param root the root node
- * @param key the key
- * @return a list with the matching nodes
+ * {@inheritDoc} This method supports the syntax as described in the class
+ * comment.
*/
- @Override
- public List<ConfigurationNode> query(ConfigurationNode root, String key)
+ public <T> List<QueryResult<T>> query(T root, String key,
+ NodeHandler<T> handler)
{
- List<ConfigurationNode> nodes = new LinkedList<ConfigurationNode>();
+ List<QueryResult<T>> results = new LinkedList<QueryResult<T>>();
findNodesForKey(new DefaultConfigurationKey(this, key).iterator(),
- root, nodes);
- return nodes;
+ root, results, handler);
+ return results;
}
/**
- * Determines the key of the passed in node. This implementation takes the
+ * {@inheritDoc} This implementation takes the
* given parent key, adds a property delimiter, and then adds the node's
- * name. (For attribute nodes the attribute delimiters are used instead.)
- * The name of the root node is a blanc string. Note that no indices will be
+ * name.
+ * The name of the root node is a blank string. Note that no indices are
* returned.
- *
- * @param node the node whose key is to be determined
- * @param parentKey the key of this node's parent
- * @return the key for the given node
*/
- @Override
- public String nodeKey(ConfigurationNode node, String parentKey)
+ public <T> String nodeKey(T node, String parentKey, NodeHandler<T> handler)
{
if (parentKey == null)
{
@@ -198,18 +189,43 @@ public class DefaultExpressionEngine imp
{
DefaultConfigurationKey key = new DefaultConfigurationKey(this,
parentKey);
- if (node.isAttribute())
- {
- key.appendAttribute(node.getName());
- }
- else
- {
- key.append(node.getName(), true);
- }
+ key.append(handler.nodeName(node), true);
return key.toString();
}
}
+ public String attributeKey(String parentKey, String attributeName)
+ {
+ DefaultConfigurationKey key =
+ new DefaultConfigurationKey(this, parentKey);
+ key.appendAttribute(attributeName);
+ return key.toString();
+ }
+
+ /**
+ * {@inheritDoc} This implementation works similar to {@code nodeKey()};
+ * however, each key returned by this method has an index (except for the
+ * root node). The parent key is prepended to the name of the current node
+ * in any case and without further checks. If it is <b>null</b>, only the
+ * name of the current node with its index is returned.
+ */
+ public <T> String canonicalKey(T node, String parentKey,
+ NodeHandler<T> handler)
+ {
+ String nodeName = handler.nodeName(node);
+ T parent = handler.getParent(node);
+ DefaultConfigurationKey key =
+ new DefaultConfigurationKey(this, parentKey);
+ key.append(StringUtils.defaultString(nodeName));
+
+ if (parent != null)
+ {
+ // this is not the root key
+ key.appendIndex(determineIndex(node, parent, nodeName, handler));
+ }
+ return key.toString();
+ }
+
/**
* <p>
* Prepares Adding the property with the specified key.
@@ -218,7 +234,7 @@ public class DefaultExpressionEngine imp
* To be able to deal with the structure supported by hierarchical
* configuration implementations the passed in key is of importance,
* especially the indices it might contain. The following example should
- * clarify this: Suppose the actual node structure looks like the
+ * clarify this: Suppose the current node structure looks like the
* following:
* </p>
* <p>
@@ -293,12 +309,13 @@ public class DefaultExpressionEngine imp
* so there cannot be any ambiguities.)
* </p>
*
+ * @param <T> the type of the nodes to be dealt with
* @param root the root node of the nodes hierarchy
* @param key the key of the new property
+ * @param handler the node handler
* @return a data object with information needed for the add operation
*/
- @Override
- public NodeAddData prepareAdd(ConfigurationNode root, String key)
+ public <T> NodeAddData<T> prepareAdd(T root, String key, NodeHandler<T> handler)
{
DefaultConfigurationKey.KeyIterator it = new DefaultConfigurationKey(
this, key).iterator();
@@ -308,8 +325,8 @@ public class DefaultExpressionEngine imp
"Key for add operation must be defined!");
}
- NodeAddData result = new NodeAddData();
- result.setParent(findLastPathNode(it, root));
+ T parent = findLastPathNode(it, root, handler);
+ List<String> pathNodes = new LinkedList<String>();
while (it.hasNext())
{
@@ -319,30 +336,32 @@ public class DefaultExpressionEngine imp
"Invalid key for add operation: " + key
+ " (Attribute key in the middle.)");
}
- result.addPathNode(it.currentKey());
+ pathNodes.add(it.currentKey());
it.next();
}
- result.setNewNodeName(it.currentKey());
- result.setAttribute(!it.isPropertyKey());
- return result;
+ return new NodeAddData<T>(parent, it.currentKey(), !it.isPropertyKey(),
+ pathNodes);
}
/**
* Recursive helper method for evaluating a key. This method processes all
* facets of a configuration key, traverses the tree of properties and
- * fetches the the nodes of all matching properties.
+ * fetches the results of all matching properties.
*
+ * @param <T> the type of nodes to be dealt with
* @param keyPart the configuration key iterator
- * @param node the actual node
- * @param nodes here the found nodes are stored
+ * @param node the current node
+ * @param results here the found results are stored
+ * @param handler the node handler
*/
- protected void findNodesForKey(DefaultConfigurationKey.KeyIterator keyPart,
- ConfigurationNode node, Collection<ConfigurationNode> nodes)
+ protected <T> void findNodesForKey(
+ DefaultConfigurationKey.KeyIterator keyPart, T node,
+ Collection<QueryResult<T>> results, NodeHandler<T> handler)
{
if (!keyPart.hasNext())
{
- nodes.add(node);
+ results.add(QueryResult.createNodeResult(node));
}
else
@@ -350,26 +369,32 @@ public class DefaultExpressionEngine imp
String key = keyPart.nextKey(false);
if (keyPart.isPropertyKey())
{
- processSubNodes(keyPart, node.getChildren(key), nodes);
+ processSubNodes(keyPart, handler.getChildren(node, key),
+ results, handler);
}
- if (keyPart.isAttribute())
+ if (keyPart.isAttribute() && !keyPart.hasNext())
{
- processSubNodes(keyPart, node.getAttributes(key), nodes);
+ if (handler.getAttributeValue(node, key) != null)
+ {
+ results.add(QueryResult.createAttributeResult(node, key));
+ }
}
}
}
/**
* Finds the last existing node for an add operation. This method traverses
- * the configuration node tree along the specified key. The last existing
- * node on this path is returned.
+ * the node tree along the specified key. The last existing node on this
+ * path is returned.
*
+ * @param <T> the type of the nodes to be dealt with
* @param keyIt the key iterator
- * @param node the actual node
+ * @param node the current node
+ * @param handler the node handler
* @return the last existing node on the given path
*/
- protected ConfigurationNode findLastPathNode(
- DefaultConfigurationKey.KeyIterator keyIt, ConfigurationNode node)
+ protected <T> T findLastPathNode(DefaultConfigurationKey.KeyIterator keyIt,
+ T node, NodeHandler<T> handler)
{
String keyPart = keyIt.nextKey(false);
@@ -382,15 +407,17 @@ public class DefaultExpressionEngine imp
"Invalid path for add operation: "
+ "Attribute key in the middle!");
}
- int idx = keyIt.hasIndex() ? keyIt.getIndex() : node
- .getChildrenCount(keyPart) - 1;
- if (idx < 0 || idx >= node.getChildrenCount(keyPart))
+ int idx =
+ keyIt.hasIndex() ? keyIt.getIndex() : handler
+ .getChildrenCount(node, keyPart) - 1;
+ if (idx < 0 || idx >= handler.getChildrenCount(node, keyPart))
{
return node;
}
else
{
- return findLastPathNode(keyIt, node.getChildren(keyPart).get(idx));
+ return findLastPathNode(keyIt,
+ handler.getChildren(node, keyPart).get(idx), handler);
}
}
@@ -405,28 +432,46 @@ public class DefaultExpressionEngine imp
* the current node depending on the type of the current key part (children,
* attributes, or both).
*
+ * @param <T> the type of the nodes to be dealt with
* @param keyPart the key part
* @param subNodes a list with the sub nodes to process
* @param nodes the target collection
+ * @param handler the node handler
*/
- private void processSubNodes(DefaultConfigurationKey.KeyIterator keyPart,
- List<ConfigurationNode> subNodes, Collection<ConfigurationNode> nodes)
+ private <T> void processSubNodes(DefaultConfigurationKey.KeyIterator keyPart,
+ List<T> subNodes, Collection<QueryResult<T>> nodes, NodeHandler<T> handler)
{
if (keyPart.hasIndex())
{
if (keyPart.getIndex() >= 0 && keyPart.getIndex() < subNodes.size())
{
findNodesForKey((DefaultConfigurationKey.KeyIterator) keyPart
- .clone(), subNodes.get(keyPart.getIndex()), nodes);
+ .clone(), subNodes.get(keyPart.getIndex()), nodes, handler);
}
}
else
{
- for (ConfigurationNode node : subNodes)
+ for (T node : subNodes)
{
findNodesForKey((DefaultConfigurationKey.KeyIterator) keyPart
- .clone(), node, nodes);
+ .clone(), node, nodes, handler);
}
}
}
+
+ /**
+ * Determines the index of the given node based on its parent node.
+ *
+ * @param node the current node
+ * @param parent the parent node
+ * @param nodeName the name of the current node
+ * @param handler the node handler
+ * @param <T> the type of the nodes to be dealt with
+ * @return the index of this node
+ */
+ private static <T> int determineIndex(T node, T parent, String nodeName,
+ NodeHandler<T> handler)
+ {
+ return handler.getChildren(parent, nodeName).indexOf(node);
+ }
}
Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ExpressionEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ExpressionEngine.java?rev=1585178&r1=1585177&r2=1585178&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ExpressionEngine.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ExpressionEngine.java Sat Apr 5 19:42:49 2014
@@ -27,56 +27,93 @@ import java.util.List;
* An <em>expression engine</em> knows how to map a key for a configuration's
* property to a single or a set of configuration nodes. Thus it defines the way
* how properties are addressed in this configuration. Methods of a
- * configuration that have to handle property key (e.g.
- * {@code getProperty()} or {@code addProperty()} do not interpret
- * the passed in keys on their own, but delegate this task to an associated
- * expression engine. This expression engine will then find out, which
- * configuration nodes are addressed by the key.
+ * configuration that have to handle property keys (e.g. {@code getProperty()}
+ * or {@code addProperty()} do not interpret the passed in keys on their own,
+ * but delegate this task to an associated expression engine. This expression
+ * engine will then find out, which configuration nodes are addressed by the
+ * key.
* </p>
* <p>
* Separating the task of evaluating property keys from the configuration object
- * has the advantage that many different expression languages (i.e. ways for
+ * has the advantage that multiple different expression languages (i.e. ways for
* querying or setting properties) can be supported. Just set a suitable
* implementation of this interface as the configuration's expression engine,
* and you can use the syntax provided by this implementation.
* </p>
+ * <p>
+ * An {@code ExpressionEngine} can deal with nodes of different types. To
+ * achieve this, a {@link NodeHandler} for the desired type must be passed to
+ * the methods.
+ * </p>
*
* @since 1.3
- * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons
- * Configuration team</a>
* @version $Id$
*/
public interface ExpressionEngine
{
/**
- * Finds the node(s) that is (are) matched by the specified key. This is the
- * main method for interpreting property keys. An implementation must
- * traverse the given root node and its children to find all nodes that are
- * matched by the given key. If the key is not correct in the syntax
- * provided by that implementation, it is free to throw a (runtime)
- * exception indicating this error condition.
+ * Finds the nodes and/or attributes that are matched by the specified key.
+ * This is the main method for interpreting property keys. An implementation
+ * must traverse the given root node and its children to find all results
+ * that are matched by the given key. If the key is not correct in the
+ * syntax provided by that implementation, it is free to throw a (runtime)
+ * exception indicating this error condition. The passed in
+ * {@code NodeHandler} can be used to gather the required information from
+ * the node object.
*
- * @param root the root node of a hierarchy of configuration nodes
+ * @param <T> the type of the node to be processed
+ * @param root the root node of a hierarchy of nodes
* @param key the key to be evaluated
- * @return a list with the nodes that are matched by the key (should never
- * be <b>null</b>)
+ * @param handler the {@code NodeHandler} for accessing the node
+ * @return a list with the results that are matched by the key (should never
+ * be <b>null</b>)
*/
- List<ConfigurationNode> query(ConfigurationNode root, String key);
+ <T> List<QueryResult<T>> query(T root, String key, NodeHandler<T> handler);
/**
* Returns the key for the specified node in the expression language
* supported by an implementation. This method is called whenever a property
* key for a node has to be constructed, e.g. by the
- * {@link org.apache.commons.configuration.Configuration#getKeys() getKeys()}
- * method.
+ * {@link org.apache.commons.configuration.Configuration#getKeys()
+ * getKeys()} method.
*
+ * @param <T> the type of the node to be processed
* @param node the node, for which the key must be constructed
* @param parentKey the key of this node's parent (can be <b>null</b> for
- * the root node)
+ * the root node)
+ * @param handler the {@code NodeHandler} for accessing the node
* @return this node's key
*/
- String nodeKey(ConfigurationNode node, String parentKey);
+ <T> String nodeKey(T node, String parentKey, NodeHandler<T> handler);
+
+ /**
+ * Returns the key of an attribute. The passed in {@code parentKey} must
+ * reference the parent node of the attribute. A concrete implementation
+ * must concatenate this parent key with the attribute name to a valid key
+ * for this attribute.
+ *
+ * @param parentKey the key to the node owning this attribute
+ * @param attributeName the name of the attribute in question
+ * @return the resulting key referencing this attribute
+ */
+ String attributeKey(String parentKey, String attributeName);
+
+ /**
+ * Determines a "canonical" key for the specified node in the
+ * expression language supported by this implementation. This means that
+ * always a unique key if generated pointing to this specific node. For most
+ * concrete implementations, this means that an index is added to the node
+ * name to ensure that there are no ambiguities with child nodes having the
+ * same names.
+ *
+ * @param <T> the type of the node to be processed
+ * @param node the node, for which the key must be constructed
+ * @param parentKey the key of this node's parent (can be <b>null</b> for
+ * the root node)
+ * @param handler the {@code NodeHandler} for accessing the node
+ * @return the canonical key of this node
+ */
+ <T> String canonicalKey(T node, String parentKey, NodeHandler<T> handler);
/**
* Returns information needed for an add operation. This method gets called
@@ -84,9 +121,11 @@ public interface ExpressionEngine
* has to interpret the specified key, find the parent node for the new
* elements, and provide all information about new nodes to be added.
*
+ * @param <T> the type of the node to be processed
* @param root the root node
* @param key the key for the new property
+ * @param handler the {@code NodeHandler} for accessing the node
* @return an object with all information needed for the add operation
*/
- NodeAddData prepareAdd(ConfigurationNode root, String key);
+ <T> NodeAddData<T> prepareAdd(T root, String key, NodeHandler<T> handler);
}
Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeAddData.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeAddData.java?rev=1585178&r1=1585177&r2=1585178&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeAddData.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeAddData.java Sat Apr 5 19:42:49 2014
@@ -16,8 +16,9 @@
*/
package org.apache.commons.configuration.tree;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -46,47 +47,41 @@ import java.util.List;
* </p>
*
* @since 1.3
- * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons
- * Configuration team</a>
* @version $Id$
+ * @param <T> the type of nodes this class can handle
*/
-public class NodeAddData
+public class NodeAddData<T>
{
/** Stores the parent node of the add operation. */
- private ConfigurationNode parent;
+ private final T parent;
/**
- * Stores a list with nodes that are on the path between the parent node and
- * the new node.
+ * Stores a list with the names of nodes that are on the path between the
+ * parent node and the new node.
*/
- private List<String> pathNodes;
+ private final List<String> pathNodes;
/** Stores the name of the new node. */
- private String newNodeName;
+ private final String newNodeName;
/** Stores the attribute flag. */
- private boolean attribute;
+ private final boolean attribute;
/**
- * Creates a new, uninitialized instance of {@code NodeAddData}.
- */
- public NodeAddData()
- {
- this(null, null);
- }
-
- /**
- * Creates a new instance of {@code NodeAddData} and sets the most
- * important data fields.
+ * Creates a new instance of {@code NodeAddData} and initializes it.
*
- * @param parent the parent node
- * @param nodeName the name of the new node
+ * @param parentNode the parent node of the add operation
+ * @param newName the name of the new node
+ * @param isAttr flag whether the new node is an attribute
+ * @param intermediateNodes an optional collection with path nodes
*/
- public NodeAddData(ConfigurationNode parent, String nodeName)
+ public NodeAddData(T parentNode, String newName, boolean isAttr,
+ Collection<String> intermediateNodes)
{
- setParent(parent);
- setNewNodeName(nodeName);
+ parent = parentNode;
+ newNodeName = newName;
+ attribute = isAttr;
+ pathNodes = createPathNodes(intermediateNodes);
}
/**
@@ -101,17 +96,6 @@ public class NodeAddData
}
/**
- * Sets the attribute flag. This flag determines whether an attribute or a
- * child node will be added.
- *
- * @param attribute the attribute flag
- */
- public void setAttribute(boolean attribute)
- {
- this.attribute = attribute;
- }
-
- /**
* Returns the name of the new node.
*
* @return the new node's name
@@ -122,39 +106,18 @@ public class NodeAddData
}
/**
- * Sets the name of the new node. A node with this name will be added to the
- * configuration's node hierarchy.
- *
- * @param newNodeName the name of the new node
- */
- public void setNewNodeName(String newNodeName)
- {
- this.newNodeName = newNodeName;
- }
-
- /**
* Returns the parent node.
*
* @return the parent node
*/
- public ConfigurationNode getParent()
+ public T getParent()
{
return parent;
}
/**
- * Sets the parent node. New nodes will be added to this node.
- *
- * @param parent the parent node
- */
- public void setParent(ConfigurationNode parent)
- {
- this.parent = parent;
- }
-
- /**
* Returns a list with further nodes that must be added. This is needed if a
- * complete branch is to be added at once. For instance imagine that there
+ * complete branch is to be added at once. For instance, imagine that there
* exists only a node {@code database}. Now the key
* {@code database.connection.settings.username} (assuming the syntax
* of the default expression engine) is to be added. Then
@@ -168,29 +131,26 @@ public class NodeAddData
*/
public List<String> getPathNodes()
{
- if (pathNodes != null)
- {
- return Collections.unmodifiableList(pathNodes);
- }
- else
- {
- return Collections.emptyList();
- }
+ return pathNodes;
}
/**
- * Adds the name of a path node. With this method an additional node to be
- * added can be defined.
+ * Creates the list with path nodes. Handles null input.
*
- * @param nodeName the name of the node
- * @see #getPathNodes()
+ * @param intermediateNodes the nodes passed to the constructor
+ * @return an unmodifiable list of path nodes
*/
- public void addPathNode(String nodeName)
+ private static List<String> createPathNodes(
+ Collection<String> intermediateNodes)
{
- if (pathNodes == null)
+ if (intermediateNodes == null)
+ {
+ return Collections.emptyList();
+ }
+ else
{
- pathNodes = new LinkedList<String>();
+ return Collections.unmodifiableList(new ArrayList<String>(
+ intermediateNodes));
}
- pathNodes.add(nodeName);
}
}
Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeHandler.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeHandler.java?rev=1585178&r1=1585177&r2=1585178&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeHandler.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeHandler.java Sat Apr 5 19:42:49 2014
@@ -17,6 +17,7 @@
package org.apache.commons.configuration.tree;
import java.util.List;
+import java.util.Set;
/**
* <p>
@@ -28,15 +29,15 @@ import java.util.List;
* {@code NodeHandler} is used. The handler provides a number of methods for
* querying the internal state of a node in a read-only way.
* </p>
- *
+ *
* @version $Id$
- * @param T the type of the nodes this handler deals with
+ * @param <T> the type of the nodes this handler deals with
*/
public interface NodeHandler<T>
{
/**
* Returns the name of the specified node
- *
+ *
* @param node the node
* @return the name of this node
*/
@@ -44,7 +45,7 @@ public interface NodeHandler<T>
/**
* Returns the value of the specified node.
- *
+ *
* @param node the node
* @return the value of this node
*/
@@ -52,7 +53,7 @@ public interface NodeHandler<T>
/**
* Returns the parent of the specified node.
- *
+ *
* @param node the node
* @return the parent node
*/
@@ -60,7 +61,7 @@ public interface NodeHandler<T>
/**
* Returns an unmodifiable list with all children of the specified node.
- *
+ *
* @param node the node
* @return a list with the child nodes of this node
*/
@@ -69,7 +70,7 @@ public interface NodeHandler<T>
/**
* Returns an unmodifiable list of all children of the specified node with
* the given name.
- *
+ *
* @param node the node
* @param name the name of the desired child nodes
* @return a list with all children with the given name
@@ -78,7 +79,7 @@ public interface NodeHandler<T>
/**
* Returns the child with the given index of the specified node.
- *
+ *
* @param node the node
* @param index the index (0-based)
* @return the child with the given index
@@ -86,18 +87,18 @@ public interface NodeHandler<T>
T getChild(T node, int index);
/**
- * Returns the index of the given child node relative to its name. This
- * method can be called when a unique identifier for a specific node is
- * needed. The node name alone might not be sufficient because there may be
- * multiple child nodes with the same name. This method returns 0 if the
- * given node is the first child node with this name, 1 for the second child
- * node and so on. If the node has no parent node or if it is an attribute,
- * -1 is returned.
- *
- * @param node a child node whose index is to be retrieved
+ * Returns the index of the given child node in the list of children of its
+ * parent. This method is the opposite operation of
+ * {@link #getChild(Object, int)}. This method returns 0 if the given node
+ * is the first child node with this name, 1 for the second child node and
+ * so on. If the node has no parent node or if it is an attribute, -1 is
+ * returned.
+ *
+ * @param parent the parent node
+ * @param child a child node whose index is to be retrieved
* @return the index of this child node
*/
- int indexOfChild(T node);
+ int indexOfChild(T parent, T child);
/**
* Returns the number of children of the specified node with the given name.
@@ -108,7 +109,7 @@ public interface NodeHandler<T>
* a child name is passed in, only the children with this name are taken
* into account. If the name <b>null</b> is passed, the total number of
* children must be returned.
- *
+ *
* @param node the node
* @param name the name of the children in question (can be <b>null</b> for
* all children)
@@ -117,17 +118,17 @@ public interface NodeHandler<T>
int getChildrenCount(T node, String name);
/**
- * Returns an unmodifiable list with the names of all attributes of the
+ * Returns an unmodifiable set with the names of all attributes of the
* specified node.
- *
+ *
* @param node the node
- * @return a list with the names of all attributes of this node
+ * @return a set with the names of all attributes of this node
*/
- List<String> getAttributes(T node);
+ Set<String> getAttributes(T node);
/**
* Returns a flag whether the passed in node has any attributes.
- *
+ *
* @param node the node
* @return a flag whether this node has any attributes
*/
@@ -137,7 +138,7 @@ public interface NodeHandler<T>
* Returns the value of the specified attribute from the given node. If a
* concrete {@code NodeHandler} supports attributes with multiple values,
* result might be a collection.
- *
+ *
* @param node the node
* @param name the name of the attribute
* @return the value of this attribute
@@ -148,7 +149,7 @@ public interface NodeHandler<T>
* Checks whether the specified node is defined. Nodes are
* "defined" if they contain any data, e.g. a value, or
* attributes, or defined children.
- *
+ *
* @param node the node to test
* @return a flag whether the passed in node is defined
*/
Added: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/QueryResult.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/QueryResult.java?rev=1585178&view=auto
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/QueryResult.java (added)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/QueryResult.java Sat Apr 5 19:42:49 2014
@@ -0,0 +1,201 @@
+/*
+ * 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 org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+/**
+ * <p>
+ * A data class representing a single query result produced by an
+ * {@link ExpressionEngine}.
+ * </p>
+ * <p>
+ * When passing a key to the {@code query()} method of {@code ExpressionEngine}
+ * the result can be a set of nodes or attributes - depending on the key. This
+ * class can represent both types of results. The aim is to give a user of
+ * {@code ExpressionEngine} all information needed for evaluating the results
+ * returned.
+ * </p>
+ * <p>
+ * Implementation note: Instances are immutable. They are created using the
+ * static factory methods.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ * @param <T> the type of the result nodes
+ */
+public final class QueryResult<T>
+{
+ /** The node result. */
+ private final T node;
+
+ /** The name of the result attribute. */
+ private final String attributeName;
+
+ /**
+ * Creates a new instance of {@code QueryResult}.
+ *
+ * @param nd the node
+ * @param attr the attribute name
+ */
+ private QueryResult(T nd, String attr)
+ {
+ node = nd;
+ attributeName = attr;
+ }
+
+ /**
+ * Creates a {@code QueryResult} instance representing the specified result
+ * node.
+ *
+ * @param <T> the type of the result node
+ * @param resultNode the result node
+ * @return the newly created instance
+ */
+ public static <T> QueryResult<T> createNodeResult(T resultNode)
+ {
+ return new QueryResult<T>(resultNode, null);
+ }
+
+ /**
+ * Creates a {@code QueryResult} instance representing an attribute result.
+ * An attribute result consists of the node the attribute belongs to and the
+ * attribute name. (The value can be obtained based on this information.)
+ *
+ * @param parentNode the node which owns the attribute
+ * @param attrName the attribute name
+ * @param <T> the type of the parent node
+ * @return the newly created instance
+ */
+ public static <T> QueryResult<T> createAttributeResult(T parentNode,
+ String attrName)
+ {
+ return new QueryResult<T>(parentNode, attrName);
+ }
+
+ /**
+ * Returns the node referenced by this object. Depending on the result type,
+ * this is either the result node or the parent node of the represented
+ * attribute.
+ *
+ * @return the referenced node
+ */
+ public T getNode()
+ {
+ return node;
+ }
+
+ /**
+ * Returns the name of the attribute. This method is defined only for
+ * results of type attribute.
+ *
+ * @return the attribute name
+ */
+ public String getAttributeName()
+ {
+ return attributeName;
+ }
+
+ /**
+ * Returns a flag whether this is a result of type attribute. If result is
+ * <b>true</b>, the attribute name and value can be queried. Otherwise, only
+ * the result node is available.
+ *
+ * @return <b>true</b> for an attribute result, <b>false</b> otherwise
+ */
+ public boolean isAttributeResult()
+ {
+ return StringUtils.isNotEmpty(getAttributeName());
+ }
+
+ /**
+ * Returns the attribute value if this is an attribute result. If this is
+ * not an attribute result, an exception is thrown.
+ *
+ * @param handler the {@code NodeHandler}
+ * @return the attribute value
+ * @throws IllegalStateException if this is not an attribute result
+ */
+ public Object getAttributeValue(NodeHandler<T> handler)
+ {
+ if (!isAttributeResult())
+ {
+ throw new IllegalStateException("This is not an attribute result! "
+ + "Attribute value cannot be fetched.");
+ }
+ return handler.getAttributeValue(getNode(), getAttributeName());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return new HashCodeBuilder().append(getNode())
+ .append(getAttributeName()).toHashCode();
+ }
+
+ /**
+ * Compares this object with another one. Two instances of
+ * {@code QueryResult} are considered equal if they are of the same result
+ * type and have the same properties.
+ *
+ * @param obj the object to compare to
+ * @return a flag whether these objects are equal
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof QueryResult))
+ {
+ return false;
+ }
+
+ QueryResult<?> c = (QueryResult<?>) obj;
+ return new EqualsBuilder().append(getNode(), c.getNode())
+ .append(getAttributeName(), c.getAttributeName()).isEquals();
+ }
+
+ /**
+ * Returns a string representation of this object. Depending on the result
+ * type either the result node or the parent node and the attribute name are
+ * contained in this string.
+ *
+ * @return a string for this object
+ */
+ @Override
+ public String toString()
+ {
+ ToStringBuilder sb = new ToStringBuilder(this);
+ if (isAttributeResult())
+ {
+ sb.append("parentNode", getNode()).append("attribute",
+ getAttributeName());
+ }
+ else
+ {
+ sb.append("resultNode", getNode());
+ }
+ return sb.toString();
+ }
+}
Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestDefaultExpressionEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestDefaultExpressionEngine.java?rev=1585178&r1=1585177&r2=1585178&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestDefaultExpressionEngine.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestDefaultExpressionEngine.java Sat Apr 5 19:42:49 2014
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.List;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
/**
@@ -51,6 +52,12 @@ public class TestDefaultExpressionEngine
{ "uid", "uname", "firstName", "lastName", "email"},
{ "docid", "name", "creationDate", "authorID", "version"}};
+ /** The root of a hierarchy with test nodes. */
+ private static ImmutableNode root;
+
+ /** A node handler for the hierarchy of test nodes. */
+ private static NodeHandler<ImmutableNode> handler;
+
/** The object to be tested. */
private DefaultExpressionEngine engine;
@@ -64,7 +71,6 @@ public class TestDefaultExpressionEngine
@Before
public void setUp() throws Exception
{
- root = setUpNodes();
engine = DefaultExpressionEngine.INSTANCE;
}
@@ -111,7 +117,8 @@ public class TestDefaultExpressionEngine
for (int i = 0; i < tables.length; i++)
{
checkKeyValue("tables.table(" + i + ").name", "name", tables[i]);
- checkKeyValue("tables.table(" + i + ")[@type]", "type", tabTypes[i]);
+ checkAttributeValue("tables.table(" + i + ")[@type]", "type",
+ tabTypes[i]);
for (int j = 0; j < fields[i].length; j++)
{
@@ -134,6 +141,7 @@ public class TestDefaultExpressionEngine
checkKey("tables.table(0).fields.field(28).name", null, 0);
checkKey("tables.table(0).fields.field().name", null, 0);
checkKey("connection.settings.usr.name", null, 0);
+ checkKey("tables.table(0)[@type].additional", null, 0);
}
/**
@@ -162,21 +170,48 @@ public class TestDefaultExpressionEngine
.create();
engine = new DefaultExpressionEngine(symbols);
checkKeyValue("tables.table(0).name", "name", tables[0]);
- checkKeyValue("tables.table(0).type", "type", tabTypes[0]);
+ checkAttributeValue("tables.table(0).type", "type", tabTypes[0]);
checkKey("tables.table.type", "type", 2);
}
/**
- * Tests accessing the root node.
+ * Helper method for testing a query for the root node.
+ *
+ * @param key the key to be used
+ */
+ private void checkQueryRootNode(String key)
+ {
+ List<QueryResult<ImmutableNode>> results = checkKey(key, null, 1);
+ QueryResult<ImmutableNode> result = results.get(0);
+ assertFalse("No node result", result.isAttributeResult());
+ assertSame("Not the root node", root, result.getNode());
+ }
+
+ /**
+ * Tests whether the root node can be retrieved using the null key.
*/
@Test
- public void testQueryRootNode()
+ public void testQueryRootNodeNullKey()
{
- List<ConfigurationNode> nodes = checkKey(null, null, 1);
- assertSame("Root node not found", root, nodes.get(0));
- nodes = checkKey("", null, 1);
- assertSame("Root node not found", root, nodes.get(0));
- checkKeyValue("[@test]", "test", "true");
+ checkQueryRootNode(null);
+ }
+
+ /**
+ * Tests whether the root node can be retrieved using the empty key.
+ */
+ @Test
+ public void testQueryRootNodeEmptyKey()
+ {
+ checkQueryRootNode("");
+ }
+
+ /**
+ * Tests whether an attribute of the root node can be queried.
+ */
+ @Test
+ public void testQueryRootAttribute()
+ {
+ checkAttributeValue("[@test]", "test", "true");
}
/**
@@ -188,8 +223,8 @@ public class TestDefaultExpressionEngine
{
setUpAlternativeSyntax();
checkKeyValue("tables/table[1]/name", "name", tables[1]);
- checkKeyValue("tables/table[0]@type", "type", tabTypes[0]);
- checkKeyValue("@test", "test", "true");
+ checkAttributeValue("tables/table[0]@type", "type", tabTypes[0]);
+ checkAttributeValue("@test", "test", "true");
checkKeyValue("connection.settings/usr.name", "usr.name", "scott");
}
@@ -199,39 +234,56 @@ public class TestDefaultExpressionEngine
@Test
public void testNodeKey()
{
- ConfigurationNode node = root.getChild(0);
+ ImmutableNode node = root.getChildren().get(0);
assertEquals("Invalid name for descendant of root", "tables", engine
- .nodeKey(node, ""));
+ .nodeKey(node, "", handler));
assertEquals("Parent key not respected", "test.tables", engine.nodeKey(
- node, "test"));
+ node, "test", handler));
assertEquals("Full parent key not taken into account",
"a.full.parent.key.tables", engine.nodeKey(node,
- "a.full.parent.key"));
+ "a.full.parent.key", handler));
}
/**
- * Tests obtaining keys when the root node is involved.
+ * Tests obtaining keys if the root node is involved.
*/
@Test
public void testNodeKeyWithRoot()
{
- assertEquals("Wrong name for root noot", "", engine.nodeKey(root, null));
- assertEquals("Null name not detected", "test", engine.nodeKey(root,
- "test"));
+ assertEquals("Wrong name for root node", "",
+ engine.nodeKey(root, null, handler));
+ assertEquals("Null name not detected", "test",
+ engine.nodeKey(root, "test", handler));
}
/**
* Tests obtaining keys for attribute nodes.
*/
@Test
- public void testNodeKeyWithAttribute()
+ public void testAttributeKey()
{
- ConfigurationNode node = root.getChild(0).getChild(0).getAttribute(0);
- assertEquals("Wrong attribute node", "type", node.getName());
assertEquals("Wrong attribute key", "tables.table[@type]", engine
- .nodeKey(node, "tables.table"));
- assertEquals("Wrong key for root attribute", "[@test]", engine.nodeKey(
- root.getAttribute(0), ""));
+ .attributeKey("tables.table", "type"));
+ }
+
+ /**
+ * Tests whether an attribute key can be queried if the root node is involved.
+ */
+ @Test
+ public void testAttributeKeyRoot()
+ {
+ assertEquals("Wrong key for root attribute", "[@test]",
+ engine.attributeKey("", "test"));
+ }
+
+ /**
+ * Tests that a null parent key is ignored when constructing an attribute key.
+ */
+ @Test
+ public void testAttributeKeyNoParent()
+ {
+ assertEquals("Wrong key for null parent", "[@test]",
+ engine.attributeKey(null, "test"));
}
/**
@@ -240,12 +292,14 @@ public class TestDefaultExpressionEngine
@Test
public void testNodeKeyWithEscapedDelimiters()
{
- ConfigurationNode node = root.getChild(1);
- assertEquals("Wrong escaped key", "connection..settings", engine
- .nodeKey(node, ""));
- assertEquals("Wrong complex escaped key",
- "connection..settings.usr..name", engine.nodeKey(node
- .getChild(0), engine.nodeKey(node, "")));
+ ImmutableNode node = root.getChildren().get(1);
+ assertEquals("Wrong escaped key", "connection..settings",
+ engine.nodeKey(node, "", handler));
+ assertEquals(
+ "Wrong complex escaped key",
+ "connection..settings.usr..name",
+ engine.nodeKey(node.getChildren().get(0),
+ engine.nodeKey(node, "", handler), handler));
}
/**
@@ -256,9 +310,19 @@ public class TestDefaultExpressionEngine
{
setUpAlternativeSyntax();
assertEquals("Wrong child key", "tables/table", engine.nodeKey(root
- .getChild(0).getChild(0), "tables"));
- assertEquals("Wrong attribute key", "@test", engine.nodeKey(root
- .getAttribute(0), ""));
+ .getChildren().get(0).getChildren().get(0), "tables", handler));
+ }
+
+ /**
+ * Tests whether a correct attribute key with alternative syntax is
+ * generated.
+ */
+ @Test
+ public void testAttributeKeyWithAlternativeSyntax()
+ {
+ setUpAlternativeSyntax();
+ assertEquals("Wrong attribute key", "@test",
+ engine.attributeKey("", "test"));
}
/**
@@ -276,7 +340,7 @@ public class TestDefaultExpressionEngine
.create();
engine = new DefaultExpressionEngine(symbols);
assertEquals("Wrong attribute key", "/test",
- engine.nodeKey(root.getAttribute(0), ""));
+ engine.attributeKey("", "test"));
}
/**
@@ -285,43 +349,43 @@ public class TestDefaultExpressionEngine
@Test
public void testPrepareAddDirectly()
{
- NodeAddData data = engine.prepareAdd(root, "newNode");
+ NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "newNode", handler);
assertSame("Wrong parent node", root, data.getParent());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
assertEquals("Wrong name of new node", "newNode", data.getNewNodeName());
assertFalse("New node is an attribute", data.isAttribute());
- data = engine.prepareAdd(root, "tables.table.fields.field.name");
+ data = engine.prepareAdd(root, "tables.table.fields.field.name", handler);
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
- assertEquals("Wrong parent node", "field", data.getParent().getName());
- ConfigurationNode nd = data.getParent().getChild(0);
- assertEquals("Field has no name node", "name", nd.getName());
+ assertEquals("Wrong parent node", "field", data.getParent().getNodeName());
+ ImmutableNode nd = data.getParent().getChildren().get(0);
+ assertEquals("Field has no name node", "name", nd.getNodeName());
assertEquals("Incorrect name", "version", nd.getValue());
}
/**
- * Tests adding when indices are involved.
+ * Tests adding if indices are involved.
*/
@Test
public void testPrepareAddWithIndex()
{
- NodeAddData data = engine
- .prepareAdd(root, "tables.table(0).tableSpace");
+ NodeAddData<ImmutableNode> data = engine
+ .prepareAdd(root, "tables.table(0).tableSpace", handler);
assertEquals("Wrong name of new node", "tableSpace", data
.getNewNodeName());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
assertEquals("Wrong type of parent node", "table", data.getParent()
- .getName());
- ConfigurationNode node = data.getParent().getChild(0);
+ .getNodeName());
+ ImmutableNode node = data.getParent().getChildren().get(0);
assertEquals("Wrong table", tables[0], node.getValue());
- data = engine.prepareAdd(root, "tables.table(1).fields.field(2).alias");
+ data = engine.prepareAdd(root, "tables.table(1).fields.field(2).alias", handler);
assertEquals("Wrong name of new node", "alias", data.getNewNodeName());
assertEquals("Wrong type of parent node", "field", data.getParent()
- .getName());
+ .getNodeName());
assertEquals("Wrong field node", "creationDate", data.getParent()
- .getChild(0).getValue());
+ .getChildren().get(0).getValue());
}
/**
@@ -330,16 +394,23 @@ public class TestDefaultExpressionEngine
@Test
public void testPrepareAddAttribute()
{
- NodeAddData data = engine.prepareAdd(root,
- "tables.table(0)[@tableSpace]");
+ NodeAddData<ImmutableNode> data = engine.prepareAdd(root,
+ "tables.table(0)[@tableSpace]", handler);
assertEquals("Wrong table node", tables[0], data.getParent()
- .getChild(0).getValue());
+ .getChildren().get(0).getValue());
assertEquals("Wrong name of new node", "tableSpace", data
.getNewNodeName());
assertTrue("Attribute not detected", data.isAttribute());
assertTrue("Path nodes available", data.getPathNodes().isEmpty());
+ }
- data = engine.prepareAdd(root, "[@newAttr]");
+ /**
+ * Tests whether an attribute to the root node can be added.
+ */
+ @Test
+ public void testPrepareAddAttributeRoot()
+ {
+ NodeAddData<ImmutableNode> data = engine.prepareAdd(root, "[@newAttr]", handler);
assertSame("Root node is not parent", root, data.getParent());
assertEquals("Wrong name of new node", "newAttr", data.getNewNodeName());
assertTrue("Attribute not detected", data.isAttribute());
@@ -351,25 +422,22 @@ public class TestDefaultExpressionEngine
@Test
public void testPrepareAddWithPath()
{
- NodeAddData data = engine.prepareAdd(root,
- "tables.table(1).fields.field(-1).name");
+ NodeAddData<ImmutableNode> data = engine.prepareAdd(root,
+ "tables.table(1).fields.field(-1).name", handler);
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
- checkNodePath(data, new String[]
- { "field"});
+ checkNodePath(data, "field");
assertEquals("Wrong type of parent node", "fields", data.getParent()
- .getName());
+ .getNodeName());
- data = engine.prepareAdd(root, "tables.table(-1).name");
+ data = engine.prepareAdd(root, "tables.table(-1).name", handler);
assertEquals("Wrong name of new node", "name", data.getNewNodeName());
- checkNodePath(data, new String[]
- { "table"});
+ checkNodePath(data, "table");
assertEquals("Wrong type of parent node", "tables", data.getParent()
- .getName());
+ .getNodeName());
- data = engine.prepareAdd(root, "a.complete.new.path");
+ data = engine.prepareAdd(root, "a.complete.new.path", handler);
assertEquals("Wrong name of new node", "path", data.getNewNodeName());
- checkNodePath(data, new String[]
- { "a", "complete", "new"});
+ checkNodePath(data, "a", "complete", "new");
assertSame("Root is not parent", root, data.getParent());
}
@@ -389,16 +457,16 @@ public class TestDefaultExpressionEngine
.getPropertyDelimiter()).create();
engine = new DefaultExpressionEngine(symbols);
- NodeAddData data = engine.prepareAdd(root, "tables.table(0).test");
+ NodeAddData<ImmutableNode> data =
+ engine.prepareAdd(root, "tables.table(0).test", handler);
assertEquals("Wrong name of new node", "test", data.getNewNodeName());
assertFalse("New node is an attribute", data.isAttribute());
assertEquals("Wrong type of parent node", "table", data.getParent()
- .getName());
+ .getNodeName());
- data = engine.prepareAdd(root, "a.complete.new.path");
+ data = engine.prepareAdd(root, "a.complete.new.path", handler);
assertFalse("New node is an attribute", data.isAttribute());
- checkNodePath(data, new String[]
- { "a", "complete", "new"});
+ checkNodePath(data, "a", "complete", "new");
}
/**
@@ -408,17 +476,17 @@ public class TestDefaultExpressionEngine
public void testPrepareAddWithAlternativeSyntax()
{
setUpAlternativeSyntax();
- NodeAddData data = engine.prepareAdd(root, "tables/table[0]/test");
+ NodeAddData<ImmutableNode> data =
+ engine.prepareAdd(root, "tables/table[0]/test", handler);
assertEquals("Wrong name of new node", "test", data.getNewNodeName());
assertFalse("New node is attribute", data.isAttribute());
- assertEquals("Wrong parent node", tables[0], data.getParent().getChild(
- 0).getValue());
+ assertEquals("Wrong parent node", tables[0], data.getParent()
+ .getChildren().get(0).getValue());
- data = engine.prepareAdd(root, "a/complete/new/path@attr");
+ data = engine.prepareAdd(root, "a/complete/new/path@attr", handler);
assertEquals("Wrong name of new attribute", "attr", data
.getNewNodeName());
- checkNodePath(data, new String[]
- { "a", "complete", "new", "path"});
+ checkNodePath(data, "a", "complete", "new", "path");
assertSame("Root is not parent", root, data.getParent());
}
@@ -429,27 +497,88 @@ public class TestDefaultExpressionEngine
@Test(expected = IllegalArgumentException.class)
public void testPrepareAddInvalidKey()
{
- engine.prepareAdd(root, "tables.table(0)[@type].new");
+ engine.prepareAdd(root, "tables.table(0)[@type].new", handler);
}
@Test(expected = IllegalArgumentException.class)
public void testPrepareAddInvalidKeyAttribute()
{
- engine
- .prepareAdd(root,
- "a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]");
+ engine.prepareAdd(
+ root,
+ "a.complete.new.path.with.an[@attribute].at.a.non.allowed[@position]",
+ handler);
}
@Test(expected = IllegalArgumentException.class)
public void testPrepareAddNullKey()
{
- engine.prepareAdd(root, null);
+ engine.prepareAdd(root, null, handler);
}
@Test(expected = IllegalArgumentException.class)
public void testPrepareAddEmptyKey()
{
- engine.prepareAdd(root, "");
+ engine.prepareAdd(root, "", handler);
+ }
+
+ /**
+ * Tests whether a canonical key can be queried if all child nodes have
+ * different names.
+ */
+ @Test
+ public void testCanonicalKeyNoDuplicates()
+ {
+ ImmutableNode node = fetchNode("tables.table(0).name");
+ assertEquals("Wrong canonical key", "table.name(0)",
+ engine.canonicalKey(node, "table", handler));
+ }
+
+ /**
+ * Tests whether duplicates are correctly resolved when querying for
+ * canonical keys.
+ */
+ @Test
+ public void testCanonicalKeyWithDuplicates()
+ {
+ ImmutableNode tab1 = fetchNode("tables.table(0)");
+ ImmutableNode tab2 = fetchNode("tables.table(1)");
+ assertEquals("Wrong key 1", "tables.table(0)",
+ engine.canonicalKey(tab1, "tables", handler));
+ assertEquals("Wrong key 2", "tables.table(1)",
+ engine.canonicalKey(tab2, "tables", handler));
+ }
+
+ /**
+ * Tests whether the parent key can be undefined when querying a canonical
+ * key.
+ */
+ @Test
+ public void testCanonicalKeyNoParentKey()
+ {
+ ImmutableNode node = fetchNode("tables.table(0).fields.field(1).name");
+ assertEquals("Wrong key", "name(0)",
+ engine.canonicalKey(node, null, handler));
+ }
+
+ /**
+ * Tests whether a canonical key for the parent node can be queried if no
+ * parent key was passed in.
+ */
+ @Test
+ public void testCanonicalKeyRootNoParentKey()
+ {
+ assertEquals("Wrong key", "", engine.canonicalKey(root, null, handler));
+ }
+
+ /**
+ * Tests whether a parent key is evaluated when determining the canonical
+ * key of the root node.
+ */
+ @Test
+ public void testCanonicalKeyRootWithParentKey()
+ {
+ assertEquals("Wrong key", "parent",
+ engine.canonicalKey(root, "parent", handler));
}
/**
@@ -469,39 +598,41 @@ public class TestDefaultExpressionEngine
*
* @return the root of the test node hierarchy
*/
- protected ConfigurationNode setUpNodes()
+ private static ImmutableNode setUpNodes()
{
- DefaultConfigurationNode rootNode = new DefaultConfigurationNode();
-
- DefaultConfigurationNode nodeTables = new DefaultConfigurationNode(
- "tables");
- rootNode.addChild(nodeTables);
+ ImmutableNode.Builder nodeTablesBuilder =
+ new ImmutableNode.Builder(tables.length);
+ nodeTablesBuilder.name("tables");
for (int i = 0; i < tables.length; i++)
{
- DefaultConfigurationNode nodeTable = new DefaultConfigurationNode(
- "table");
- nodeTables.addChild(nodeTable);
- nodeTable.addChild(new DefaultConfigurationNode("name", tables[i]));
- nodeTable.addAttribute(new DefaultConfigurationNode("type",
- tabTypes[i]));
- DefaultConfigurationNode nodeFields = new DefaultConfigurationNode(
- "fields");
- nodeTable.addChild(nodeFields);
+ ImmutableNode.Builder nodeTableBuilder =
+ new ImmutableNode.Builder(2);
+ nodeTableBuilder.name("table");
+ nodeTableBuilder.addChild(new ImmutableNode.Builder().name("name")
+ .value(tables[i]).create());
+ nodeTableBuilder.addAttribute("type", tabTypes[i]);
+ ImmutableNode.Builder nodeFieldsBuilder =
+ new ImmutableNode.Builder(fields[i].length);
for (int j = 0; j < fields[i].length; j++)
{
- nodeFields.addChild(createFieldNode(fields[i][j]));
+ nodeFieldsBuilder.addChild(createFieldNode(fields[i][j]));
}
+ nodeTableBuilder
+ .addChild(nodeFieldsBuilder.name("fields").create());
+ nodeTablesBuilder.addChild(nodeTableBuilder.create());
}
- DefaultConfigurationNode nodeConn = new DefaultConfigurationNode(
- "connection.settings");
- rootNode.addChild(nodeConn);
- nodeConn.addChild(new DefaultConfigurationNode("usr.name", "scott"));
- nodeConn.addChild(new DefaultConfigurationNode("usr.pwd", "tiger"));
- rootNode.addAttribute(new DefaultConfigurationNode("test", "true"));
+ ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
+ rootBuilder.addChild(nodeTablesBuilder.create());
+ ImmutableNode.Builder nodeConnBuilder = new ImmutableNode.Builder();
+ nodeConnBuilder.name("connection.settings");
+ nodeConnBuilder.addChild(createNode("usr.name", "scott"));
+ nodeConnBuilder.addChild(createNode("usr.pwd", "tiger"));
+ rootBuilder.addAttribute("test", "true");
+ rootBuilder.addChild(nodeConnBuilder.create());
- return rootNode;
+ return rootBuilder.create();
}
/**
@@ -526,19 +657,55 @@ public class TestDefaultExpressionEngine
* @param count the number of expected result nodes
* @return the list with the results of the query
*/
- private List<ConfigurationNode> checkKey(String key, String name, int count)
+ private List<QueryResult<ImmutableNode>> checkKey(String key, String name,
+ int count)
{
- List<ConfigurationNode> nodes = engine.query(root, key);
- assertEquals("Wrong number of result nodes for key " + key, count,
- nodes.size());
- for (ConfigurationNode configurationNode : nodes) {
- assertEquals("Wrong result node for key " + key, name,
- configurationNode.getName());
+ List<QueryResult<ImmutableNode>> nodes = query(key, count);
+ for (QueryResult<ImmutableNode> result : nodes)
+ {
+ if (result.isAttributeResult())
+ {
+ assertEquals("Wrong attribute name for key " + key, name,
+ result.getAttributeName());
+ }
+ else
+ {
+ assertEquals("Wrong result node for key " + key, name, result
+ .getNode().getNodeName());
+ }
}
return nodes;
}
/**
+ * Helper method for querying the test engine for a specific key.
+ *
+ * @param key the key
+ * @param expCount the expected number of result nodes
+ * @return the collection of retrieved nodes
+ */
+ private List<QueryResult<ImmutableNode>> query(String key, int expCount)
+ {
+ List<QueryResult<ImmutableNode>> nodes = engine.query(root, key, handler);
+ assertEquals("Wrong number of result nodes for key " + key, expCount,
+ nodes.size());
+ return nodes;
+ }
+
+ /**
+ * Helper method for fetching a specific node by its key.
+ *
+ * @param key the key
+ * @return the node with this key
+ */
+ private ImmutableNode fetchNode(String key)
+ {
+ QueryResult<ImmutableNode> result = query(key, 1).get(0);
+ assertFalse("An attribute result", result.isAttributeResult());
+ return result.getNode();
+ }
+
+ /**
* Helper method for checking the value of a node specified by the given
* key. This method evaluates the key and checks whether the resulting node
* has the expected value.
@@ -549,9 +716,28 @@ public class TestDefaultExpressionEngine
*/
private void checkKeyValue(String key, String name, String value)
{
- List<ConfigurationNode> nodes = checkKey(key, name, 1);
+ List<QueryResult<ImmutableNode>> results = checkKey(key, name, 1);
+ QueryResult<ImmutableNode> result = results.get(0);
+ assertFalse("No node result", result.isAttributeResult());
assertEquals("Wrong value for key " + key, value,
- nodes.get(0).getValue());
+ result.getNode().getValue());
+ }
+
+ /**
+ * Helper method for checking whether an attribute key is correctly
+ * evaluated.
+ *
+ * @param key the attribute key
+ * @param attr the attribute name
+ * @param expValue the expected attribute value
+ */
+ private void checkAttributeValue(String key, String attr, Object expValue)
+ {
+ List<QueryResult<ImmutableNode>> results = checkKey(key, attr, 1);
+ QueryResult<ImmutableNode> result = results.get(0);
+ assertTrue("Not an attribute result", result.isAttributeResult());
+ assertEquals("Wrong attribute value for key " + key, expValue,
+ result.getAttributeValue(handler));
}
/**
@@ -560,7 +746,8 @@ public class TestDefaultExpressionEngine
* @param data the add data object
* @param expected the expected path nodes
*/
- private void checkNodePath(NodeAddData data, String[] expected)
+ private void checkNodePath(NodeAddData<ImmutableNode> data,
+ String... expected)
{
assertEquals("Wrong number of path nodes", expected.length, data
.getPathNodes().size());
@@ -578,11 +765,22 @@ public class TestDefaultExpressionEngine
* @param name the name of the field
* @return the field node
*/
- private static ConfigurationNode createFieldNode(String name)
+ private static ImmutableNode createFieldNode(String name)
+ {
+ ImmutableNode.Builder nodeFieldBuilder = new ImmutableNode.Builder(1);
+ nodeFieldBuilder.addChild(createNode("name", name));
+ return nodeFieldBuilder.name("field").create();
+ }
+
+ /**
+ * Convenience method for creating a simple node with a name and a value.
+ *
+ * @param name the node name
+ * @param value the node value
+ * @return the node instance
+ */
+ private static ImmutableNode createNode(String name, Object value)
{
- DefaultConfigurationNode nodeField = new DefaultConfigurationNode(
- "field");
- nodeField.addChild(new DefaultConfigurationNode("name", name));
- return nodeField;
+ return new ImmutableNode.Builder().name(name).value(value).create();
}
}