commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1575753 - in /commons/proper/configuration/branches/immutableNodes/src: main/java/org/apache/commons/configuration/tree/ test/java/org/apache/commons/configuration/tree/
Date Sun, 09 Mar 2014 20:54:23 GMT
Author: oheger
Date: Sun Mar  9 20:54:23 2014
New Revision: 1575753

URL: http://svn.apache.org/r1575753
Log:
Added NodeSelector class.

This class allows selecting a specific node based on a single key or a chain
of keys.

Added:
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeSelector.java
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestNodeSelector.java
Modified:
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/NodeStructureHelper.java

Added: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeSelector.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeSelector.java?rev=1575753&view=auto
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeSelector.java
(added)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeSelector.java
Sun Mar  9 20:54:23 2014
@@ -0,0 +1,199 @@
+/*
+ * 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.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+/**
+ * <p>
+ * A class for selecting a specific node based on a key or a set of keys.
+ * </p>
+ * <p>
+ * An instance of this class is initialized with the key of a node. It is also
+ * possible to concatenate multiple keys - e.g. if a sub key is to be
+ * constructed from another sub key. {@code NodeSelector} provides the
+ * {@code select()} method which evaluates the wrapped keys on a specified root
+ * node and returns the resulting unique target node. The class expects that the
+ * key(s) stored in an instance select exactly one target node. If this is not
+ * the case, result is <b>null</b> indicating that the selection criteria are
+ * not sufficient.
+ * </p>
+ * <p>
+ * Implementation node: Instances of this class are immutable. They can be
+ * shared between arbitrary components.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public class NodeSelector
+{
+    /** Stores the wrapped keys. */
+    private final List<String> nodeKeys;
+
+    /**
+     * Creates a new instance of {@code NodeSelector} and initializes it with
+     * the key to the target node.
+     *
+     * @param key the key
+     */
+    public NodeSelector(String key)
+    {
+        this(Collections.singletonList(key));
+    }
+
+    /**
+     * Creates a new instance of {@code NodeSelector} and initializes it with
+     * the list of keys to be used as selection criteria.
+     *
+     * @param keys the keys for selecting nodes
+     */
+    private NodeSelector(List<String> keys)
+    {
+        nodeKeys = keys;
+    }
+
+    /**
+     * Applies this {@code NodeSelector} on the specified root node. This method
+     * applies the selection criteria stored in this object and tries to
+     * determine a single target node. If this is successful, the target node is
+     * returned. Otherwise, result is <b>null</b>.
+     *
+     * @param root the root node on which to apply this selector
+     * @param resolver the {@code NodeKeyResolver}
+     * @param handler the {@code NodeHandler}
+     * @return the selected target node or <b>null</b>
+     */
+    public ImmutableNode select(ImmutableNode root,
+            NodeKeyResolver<ImmutableNode> resolver,
+            NodeHandler<ImmutableNode> handler)
+    {
+        List<ImmutableNode> nodes = new LinkedList<ImmutableNode>();
+        Iterator<String> itKeys = nodeKeys.iterator();
+        getFilteredResults(root, resolver, handler, itKeys.next(), nodes);
+
+        while (itKeys.hasNext())
+        {
+            String currentKey = itKeys.next();
+            List<ImmutableNode> currentResults =
+                    new LinkedList<ImmutableNode>();
+            for (ImmutableNode currentRoot : nodes)
+            {
+                getFilteredResults(currentRoot, resolver, handler, currentKey,
+                        currentResults);
+            }
+            nodes = currentResults;
+        }
+
+        return (nodes.size() == 1) ? nodes.get(0) : null;
+    }
+
+    /**
+     * Creates a sub {@code NodeSelector} object which uses the key(s) of this
+     * selector plus the specified key as selection criteria. This is useful
+     * when another selection is to be performed on the results of a first
+     * selector.
+     *
+     * @param subKey the additional key for the sub selector
+     * @return the sub {@code NodeSelector} instance
+     */
+    public NodeSelector subSelector(String subKey)
+    {
+        List<String> keys = new ArrayList<String>(nodeKeys.size() + 1);
+        keys.addAll(nodeKeys);
+        keys.add(subKey);
+        return new NodeSelector(keys);
+    }
+
+    /**
+     * Compares this object with another one. Two instances of
+     * {@code NodeSelector} are considered equal if they have the same keys as
+     * selection criteria.
+     *
+     * @param obj the object to be compared
+     * @return a flag whether these objects are equal
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (!(obj instanceof NodeSelector))
+        {
+            return false;
+        }
+
+        NodeSelector c = (NodeSelector) obj;
+        return nodeKeys.equals(c.nodeKeys);
+    }
+
+    /**
+     * Returns a hash code for this object.
+     *
+     * @return a hash code
+     */
+    @Override
+    public int hashCode()
+    {
+        return nodeKeys.hashCode();
+    }
+
+    /**
+     * Returns a string representation for this object. This string contains the
+     * keys to be used as selection criteria.
+     *
+     * @return a string for this object
+     */
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this).append("keys", nodeKeys).toString();
+    }
+
+    /**
+     * Executes a query for a given key and filters the results for nodes only.
+     *
+     * @param root the root node for the query
+     * @param resolver the {@code NodeKeyResolver}
+     * @param handler the {@code NodeHandler}
+     * @param key the key
+     * @param nodes here the results are stored
+     */
+    private void getFilteredResults(ImmutableNode root,
+            NodeKeyResolver<ImmutableNode> resolver,
+            NodeHandler<ImmutableNode> handler, String key,
+            List<ImmutableNode> nodes)
+    {
+        List<QueryResult<ImmutableNode>> results =
+                resolver.resolveKey(root, key, handler);
+        for (QueryResult<ImmutableNode> result : results)
+        {
+            if (!result.isAttributeResult())
+            {
+                nodes.add(result.getNode());
+            }
+        }
+    }
+}

Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/NodeStructureHelper.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/NodeStructureHelper.java?rev=1575753&r1=1575752&r2=1575753&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/NodeStructureHelper.java
(original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/NodeStructureHelper.java
Sun Mar  9 20:54:23 2014
@@ -93,7 +93,7 @@ public class NodeStructureHelper
             {
                     "uid", "uname", "firstName", "lastName", "email"
             }, {
-                    "docid", "name", "creationDate", "authorID", "version"
+                    "docid", "name", "creationDate", "authorID", "version", "length"
             }
     };
 

Added: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestNodeSelector.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestNodeSelector.java?rev=1575753&view=auto
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestNodeSelector.java
(added)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestNodeSelector.java
Sun Mar  9 20:54:23 2014
@@ -0,0 +1,284 @@
+/*
+ * 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 static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.configuration.ConfigurationAssert;
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test class for {@code NodeSelector}.
+ *
+ * @version $Id$
+ */
+public class TestNodeSelector
+{
+    /** Constant for a test key. */
+    private static final String KEY = "tables.testKey";
+
+    /** The root node for query operations. */
+    private static ImmutableNode root;
+
+    /** A NodeKeyResolver implementation. */
+    private static NodeKeyResolver<ImmutableNode> resolver;
+
+    /** The node handler object. */
+    private static NodeHandler<ImmutableNode> handler;
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception
+    {
+        resolver = createResolver();
+        handler = new InMemoryNodeModel();
+        root =
+                new ImmutableNode.Builder(1).addChild(
+                        NodeStructureHelper.ROOT_TABLES_TREE).create();
+    }
+
+    /**
+     * Creates a {@code NodeKeyResolver} object to be used in the tests. This
+     * resolver object uses a {@code DefaultExpressionEngine} instance for
+     * resolving keys. Methods for obtaining add or update data objects are not
+     * implemented.
+     *
+     * @return the {@code NodeKeyResolver}
+     */
+    private static NodeKeyResolver<ImmutableNode> createResolver()
+    {
+        return new NodeKeyResolver<ImmutableNode>()
+        {
+            public List<QueryResult<ImmutableNode>> resolveKey(
+                    ImmutableNode root, String key,
+                    NodeHandler<ImmutableNode> handler)
+            {
+                return DefaultExpressionEngine.INSTANCE.query(root, key,
+                        handler);
+            }
+
+            public NodeAddData<ImmutableNode> resolveAddKey(ImmutableNode root,
+                    String key, NodeHandler<ImmutableNode> handler)
+            {
+                throw new UnsupportedOperationException(
+                        "Unexpected method call!");
+            }
+
+            public NodeUpdateData<ImmutableNode> resolveUpdateKey(
+                    ImmutableNode root, String key, Object newValue,
+                    NodeHandler<ImmutableNode> handler)
+            {
+                throw new UnsupportedOperationException(
+                        "Unexpected method call!");
+            }
+        };
+    }
+
+    /**
+     * Creates a mock for a resolver.
+     *
+     * @return the resolver mock
+     */
+    private static NodeKeyResolver<ImmutableNode> createResolverMock()
+    {
+        @SuppressWarnings("unchecked")
+        NodeKeyResolver<ImmutableNode> mock =
+                EasyMock.createMock(NodeKeyResolver.class);
+        return mock;
+    }
+
+    /**
+     * Tests a successful select operation for a single key.
+     */
+    @Test
+    public void testSelectSingleKeySuccess()
+    {
+        NodeSelector selector = new NodeSelector("tables.table(0).name");
+        ImmutableNode target = selector.select(root, resolver, handler);
+        assertEquals("Wrong name", "name", target.getNodeName());
+        assertEquals("Wrong value", NodeStructureHelper.table(0),
+                target.getValue());
+    }
+
+    /**
+     * Tests a select operation if the key selects an attribute node.
+     */
+    @Test
+    public void testSelectSingleAttributeKey()
+    {
+        NodeKeyResolver<ImmutableNode> resolverMock = createResolverMock();
+        EasyMock.expect(resolverMock.resolveKey(root, KEY, handler)).andReturn(
+                Collections.singletonList(QueryResult.createAttributeResult(
+                        root, KEY)));
+        EasyMock.replay(resolverMock);
+
+        NodeSelector selector = new NodeSelector(KEY);
+        assertNull("Got a result", selector.select(root, resolverMock, handler));
+    }
+
+    /**
+     * Tests whether attribute results are ignored when evaluating the key.
+     */
+    @Test
+    public void testSelectIgnoreAttributeResults()
+    {
+        NodeKeyResolver<ImmutableNode> resolverMock = createResolverMock();
+        List<QueryResult<ImmutableNode>> results =
+                new LinkedList<QueryResult<ImmutableNode>>();
+        results.add(QueryResult.createAttributeResult(
+                NodeStructureHelper.nodeForKey(root, "tables/table(0)"), "type"));
+        ImmutableNode target =
+                NodeStructureHelper.nodeForKey(root, "tables/table(1)");
+        results.add(QueryResult.createNodeResult(target));
+        results.add(QueryResult.createAttributeResult(NodeStructureHelper
+                .nodeForKey(root, "tables/table(0)/fields/field(1)"), "type"));
+        EasyMock.expect(resolverMock.resolveKey(root, KEY, handler)).andReturn(
+                results);
+        EasyMock.replay(resolverMock);
+
+        NodeSelector selector = new NodeSelector(KEY);
+        assertSame("Wrong target", target,
+                selector.select(root, resolverMock, handler));
+    }
+
+    /**
+     * Tests a select operation with a key yielding multiple target nodes.
+     */
+    @Test
+    public void testSelectMultipleTargets()
+    {
+        NodeSelector selector = new NodeSelector("tables.table.name");
+        assertNull("Got a result", selector.select(root, resolver, handler));
+    }
+
+    /**
+     * Tests a select operation with a sub key.
+     */
+    @Test
+    public void testSelectSubKey()
+    {
+        NodeSelector selectorParent = new NodeSelector("tables.table(0)");
+        NodeSelector selector =
+                selectorParent.subSelector("fields.field(1).name");
+        ImmutableNode target = selector.select(root, resolver, handler);
+        assertEquals("Wrong node selected", NodeStructureHelper.field(0, 1),
+                target.getValue());
+    }
+
+    /**
+     * Tests select() if a key is used which does not yield any results.
+     */
+    @Test
+    public void testSelectSubKeyUnknown()
+    {
+        NodeSelector selectorParent = new NodeSelector("tables.unknown");
+        NodeSelector selector =
+                selectorParent.subSelector("fields.field(1).name");
+        assertNull("Got a result", selector.select(root, resolver, handler));
+    }
+
+    /**
+     * Tests a select operation with a sub key which produces multiple results.
+     */
+    @Test
+    public void testSelectSubKeyMultipleResults()
+    {
+        NodeSelector selectorParent = new NodeSelector("tables.table");
+        NodeSelector selector =
+                selectorParent.subSelector("fields.field(1).name");
+        assertNull("Got a result", selector.select(root, resolver, handler));
+    }
+
+    /**
+     * Tests a select operation with a sub key which requires complex
+     * processing: The first kes produce multiple results; the final key reduces
+     * the result set to a single node.
+     */
+    @Test
+    public void testSelectSubKeyComplexEvaluation()
+    {
+        NodeSelector first = new NodeSelector("tables.table");
+        NodeSelector second = first.subSelector("fields");
+        int fldIdx = NodeStructureHelper.fieldsLength(1) - 1;
+        NodeSelector selector =
+                second.subSelector("field(" + fldIdx + ").name");
+        ImmutableNode target = selector.select(root, resolver, handler);
+        assertEquals("Wrong target node", NodeStructureHelper.field(1, fldIdx),
+                target.getValue());
+    }
+
+    /**
+     * Tests equals() if the expected result is true.
+     */
+    @Test
+    public void testEqualsTrue()
+    {
+        NodeSelector selector = new NodeSelector(KEY);
+        ConfigurationAssert.checkEquals(selector, selector, true);
+        NodeSelector sel2 = new NodeSelector(KEY);
+        ConfigurationAssert.checkEquals(selector, sel2, true);
+        NodeSelector sub1 = selector.subSelector("k2");
+        NodeSelector sub2 = sel2.subSelector("k2");
+        ConfigurationAssert.checkEquals(sub1, sub2, true);
+    }
+
+    /**
+     * Tests equals() if the expected result is false.
+     */
+    @Test
+    public void testEqualsFalse()
+    {
+        NodeSelector selector = new NodeSelector(KEY);
+        NodeSelector sel2 = new NodeSelector("other" + KEY);
+        ConfigurationAssert.checkEquals(selector, sel2, false);
+        sel2 = new NodeSelector(KEY).subSelector(KEY);
+        ConfigurationAssert.checkEquals(selector, sel2, false);
+    }
+
+    /**
+     * Tests equals() with other objects.
+     */
+    @Test
+    public void testEqualsOtherObjects()
+    {
+        NodeSelector selector = new NodeSelector(KEY);
+        ConfigurationAssert.checkEquals(selector, null, false);
+        ConfigurationAssert.checkEquals(selector, this, false);
+    }
+
+    /**
+     * Tests the string representation.
+     */
+    @Test
+    public void testToString()
+    {
+        final String key2 = "anotherSelectionKey";
+        NodeSelector selector = new NodeSelector(KEY).subSelector(key2);
+        String s = selector.toString();
+        assertThat(s, containsString(KEY));
+        assertThat(s, containsString(key2));
+    }
+}



Mime
View raw message