Author: oheger
Date: Sat Aug 15 20:13:06 2009
New Revision: 804523
URL: http://svn.apache.org/viewvc?rev=804523&view=rev
Log:
Added HierarchicalSourceAdapter class for transforming a plain
ConfigurationSource into a hierarchical one.
Added:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
(with props)
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
(with props)
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/ConfigurationImpl.java
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/ConfigurationImpl.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/ConfigurationImpl.java?rev=804523&r1=804522&r2=804523&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/ConfigurationImpl.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/ConfigurationImpl.java
Sat Aug 15 20:13:06 2009
@@ -58,8 +58,7 @@
* </p>
*
* @author Commons Configuration team
- * @version $Id: AbstractHierarchicalConfiguration.java 786440 2009-06-19
- * 10:35:01Z ebourg $
+ * @version $Id$
* @since 2.0
* @param <T> the type of the nodes this configuration deals with
*/
Added:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java?rev=804523&view=auto
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
(added)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
Sat Aug 15 20:13:06 2009
@@ -0,0 +1,407 @@
+/*
+ * 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.base;
+
+import java.util.Iterator;
+
+import org.apache.commons.configuration2.expr.NodeHandler;
+import org.apache.commons.configuration2.tree.ConfigurationNode;
+
+/**
+ * <p>
+ * An adapter implementation for converting a "flat"
+ * {...@link ConfigurationSource} into a hierarchical one.
+ * </p>
+ * <p>
+ * {...@link ConfigurationImpl}, the main implementation of the
+ * {...@link Configuration} interface, requires a hierarchical configuration
source
+ * for accessing configuration settings. It does not work with sources
+ * implementing only the {...@link ConfigurationSource} interface out of the
box.
+ * With this adapter class a {...@link ConfigurationSource} object can be
treated
+ * as a {...@link HierarchicalConfigurationSource}.
+ * </p>
+ * <p>
+ * The idea behind this class is that it dynamically populates a
+ * {...@link ConfigurationImpl} object (living in memory) with the properties
+ * stored in the configuration source. This causes the data to be stored in a
+ * truly hierarchical structure enabling sophisticated queries. The in-memory
+ * configuration source of this configuration is then used to implement the
+ * methods of the {...@link HierarchicalConfigurationSource} interface.
+ * </p>
+ * <p>
+ * Changes at the data of the configuration used for the transformation are not
+ * written back into the original {...@link ConfigurationSource} automatically.
+ * This can be done by calling the {...@code writeBack()} method. This will
clear
+ * the original source and then copy all data from this source into it.
+ * </p>
+ * <p>
+ * It is possible to configure this adapter to register as an event listener at
+ * the original {...@link ConfigurationSource}. Every change event fired by the
+ * {...@link ConfigurationSource} causes the configuration used internally to
be
+ * re-constructed with the current data of the {...@link ConfigurationSource}.
This
+ * will throw away all changes made at this hierarchical source! So this mode
+ * should only be used for providing a read-only hierarchical view for a plain
+ * {...@link ConfigurationSource}.
+ * </p>
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public class HierarchicalSourceAdapter implements
+ HierarchicalConfigurationSource<ConfigurationNode>,
+ ConfigurationSourceListener
+{
+ /**
+ * Stores the original source that is transformed by this adapter.
+ */
+ private final ConfigurationSource originalSource;
+
+ /** The configuration used internally for the transformation. */
+ private Configuration<ConfigurationNode> transformedConfig;
+
+ /**
+ * A flag whether the original configuration source should be monitored to
+ * react on changes.
+ */
+ private final boolean monitorChanges;
+
+ /**
+ * Creates a new instance of {...@code HierarchicalSourceAdapter} and
+ * initializes it with the {...@code ConfigurationSource} to wrap and the
flag
+ * whether changes of this {...@code ConfigurationSource} should be
monitored.
+ *
+ * @param wrappedSource the original {...@code ConfigurationSource} (must
not
+ * be <b>null</b>)
+ * @param monitorChanges the flag whether changes of the original source
+ * should cause this source to update its data
+ * @throws IllegalArgumentException if the {...@code ConfigurationSource}
is
+ * <b>null</b>
+ */
+ public HierarchicalSourceAdapter(ConfigurationSource wrappedSource,
+ boolean monitorChanges)
+ {
+ if (wrappedSource == null)
+ {
+ throw new IllegalArgumentException(
+ "Original ConfigurationSource must not be null!");
+ }
+
+ originalSource = wrappedSource;
+ this.monitorChanges = monitorChanges;
+
+ if (monitorChanges)
+ {
+ wrappedSource.addConfigurationSourceListener(this);
+ }
+ }
+
+ /**
+ * Creates a new instance of {...@code HierarchicalSourceAdapter} and
+ * initializes it with the {...@code ConfigurationSource} to wrap. Changes
of
+ * the wrapped source are not monitored.
+ *
+ * @param wrappedSource the original {...@code ConfigurationSource} (must
not
+ * be <b>null</b>)
+ * @throws IllegalArgumentException if the {...@code ConfigurationSource}
is
+ * <b>null</b>
+ */
+ public HierarchicalSourceAdapter(ConfigurationSource wrappedSource)
+ {
+ this(wrappedSource, false);
+ }
+
+ /**
+ * An utility method for copying the content of the specified {...@code
+ * ConfigurationSource} into the given {...@code Configuration}. This class
+ * iterates over all properties stored in the {...@code
ConfigurationSource}
+ * and adds their values to the {...@code Configuration}. Existing values
in
+ * the {...@code Configuration} are not overridden, but new values are
added.
+ *
+ * @param config the target {...@code Configuration} (must not be
<b>null</b>)
+ * @param source the {...@code ConfigurationSource} to be copied (must not
be
+ * <b>null</b>)
+ * @throws IllegalArgumentException if a required parameter is <b>null</b>
+ */
+ public static void fillConfiguration(Configuration<?> config,
+ ConfigurationSource source)
+ {
+ if (config == null)
+ {
+ throw new IllegalArgumentException(
+ "Configuration must not be null!");
+ }
+ if (source == null)
+ {
+ throw new IllegalArgumentException(
+ "ConfigurationSource must not be null!");
+ }
+
+ doFillConfiguration(config, source);
+ }
+
+ /**
+ * An utility method for copying the content of the specified {...@code
+ * Configuration} into the given {...@code ConfigurationSource}. This
method is
+ * the opposite of
+ * {...@link #fillConfiguration(Configuration, ConfigurationSource)}: It
+ * iterates over the keys in the {...@code Configuration} and adds their
values
+ * to the {...@code ConfigurationSource}.
+ *
+ * @param source the target {...@code ConfigurationSource} (must not be
+ * <b>null</b>)
+ * @param config the {...@code Configuration} to be copied (must not be
+ * <b>null</b>)
+ * @throws IllegalArgumentException if a required parameter is <b>null</b>
+ */
+ public static void fillSource(ConfigurationSource source,
+ Configuration<?> config)
+ {
+ if (source == null)
+ {
+ throw new IllegalArgumentException(
+ "ConfigurationSource must not be null!");
+ }
+ if (config == null)
+ {
+ throw new IllegalArgumentException(
+ "Configuration must not be null!");
+ }
+
+ doFillSource(source, config);
+ }
+
+ /**
+ * Returns the original {...@code ConfigurationSource} that is wrapped by
this
+ * adapter.
+ *
+ * @return the original {...@code ConfigurationSource}
+ */
+ public ConfigurationSource getOriginalSource()
+ {
+ return originalSource;
+ }
+
+ /**
+ * Returns a flag whether changes of the original {...@code
+ * ConfigurationSource} are monitored. A value of <b>true</b> means that
+ * this adapter is registered as a change listener at the configuration
+ * time. Every time a change event is received the data of this {...@code
+ * HierarchicalConfigurationSource} is invalidated, so it has to be
+ * re-constructed on next access.
+ *
+ * @return a flag whether changes of the wrapped source are monitored
+ */
+ public boolean isMonitorChanges()
+ {
+ return monitorChanges;
+ }
+
+ /**
+ * Writes the data stored in this {...@code
HierarchicalConfigurationSource}
+ * into the original {...@code ConfigurationSource}. This method can be
called
+ * to apply changes made at this source to the original source. Note that
+ * the original {...@code ConfigurationSource} may not be capable to fully
deal
+ * with the hierarchical structure of the data stored in this {...@code
+ * HierarchicalConfigurationSource}. So some structure might be lost during
+ * this conversion.
+ */
+ public void writeBack()
+ {
+ if (isMonitorChanges())
+ {
+ // first de-register, so we do not receive our own updates
+ getOriginalSource().removeConfigurationSourceListener(this);
+ }
+
+ try
+ {
+ getOriginalSource().clear();
+ doFillSource(getOriginalSource(), getTransformedConfiguration());
+ }
+ finally
+ {
+ if (isMonitorChanges())
+ {
+ getOriginalSource().addConfigurationSourceListener(this);
+ }
+ }
+ }
+
+ /**
+ * Adds a {...@code ConfigurationSourceListener} to this source. This
+ * implementation delegates to the transformed source.
+ *
+ * @param l the listener to add
+ */
+ public void addConfigurationSourceListener(ConfigurationSourceListener l)
+ {
+ getTransformedSource().addConfigurationSourceListener(l);
+ }
+
+ /**
+ * Removes all data from this source. This implementation delegates to the
+ * transformed source.
+ */
+ public void clear()
+ {
+ getTransformedSource().clear();
+ }
+
+ /**
+ * Returns the {...@code NodeHandler} of this source. This implementation
+ * delegates to the transformed source.
+ *
+ * @return the {...@code NodeHandler} of this source
+ */
+ public NodeHandler<ConfigurationNode> getNodeHandler()
+ {
+ return getTransformedSource().getNodeHandler();
+ }
+
+ /**
+ * Returns the root node of this source. This implementation delegates to
+ * the transformed source.
+ *
+ * @return the root node of this source
+ */
+ public ConfigurationNode getRootNode()
+ {
+ return getTransformedSource().getRootNode();
+ }
+
+ /**
+ * Removes the specified {...@code ConfigurationSourceListener} from this
+ * source. This implementation delegates to the transformed source.
+ *
+ * @param l the listener to remove
+ * @return a flag whether the listener could be removed
+ */
+ public boolean removeConfigurationSourceListener(
+ ConfigurationSourceListener l)
+ {
+ return getTransformedSource().removeConfigurationSourceListener(l);
+ }
+
+ /**
+ * Sets the root node of this source. This implementation delegates to the
+ * transformed source.
+ *
+ * @param root the new root node
+ */
+ public void setRootNode(ConfigurationNode root)
+ {
+ getTransformedSource().setRootNode(root);
+ }
+
+ /**
+ * Notifies this object about a change of a monitored {...@code
+ * ConfigurationSource}. If this {...@code HierarchicalSourceAdapter} was
+ * constructed with the {...@code monitorChanges} flag set to <b>true</b>,
it
+ * has registered itself as a change listener at the original {...@code
+ * ConfigurationSource}. Thus it receives change notifications whenever the
+ * original {...@code ConfigurationSource} is manipulated. This
implementation
+ * just calls {...@link #invalidate()} which indicates that the data of
this
+ * source has to be re-constructed.
+ *
+ * @param event the change event
+ */
+ public void configurationSourceChanged(ConfigurationSourceEvent event)
+ {
+ invalidate();
+ }
+
+ /**
+ * Returns a {...@code Configuration} that is used internally for the
+ * transformation of the original source to a hierarchical one. This
+ * configuration is populated with the data of the original source.
Whenever
+ * a change in the original source is detected, this configuration is
+ * invalidated so that it has to be re-constructed.
+ *
+ * @return the {...@code Configuration} used internally for the
transformation
+ */
+ protected synchronized Configuration<ConfigurationNode>
getTransformedConfiguration()
+ {
+ if (transformedConfig == null)
+ {
+ // need to re-construct the configuration
+ transformedConfig = new ConfigurationImpl<ConfigurationNode>(
+ new InMemoryConfigurationSource());
+ doFillConfiguration(transformedConfig, getOriginalSource());
+ }
+
+ return transformedConfig;
+ }
+
+ /**
+ * Returns the <em>transformed source</em>. This is a true hierarchical
+ * configuration source to which all operations on this adapter are
+ * delegated. This implementation uses an in-memory configuration source
+ * that is associated with a configuration to be populated and manipulated.
+ *
+ * @return the transformed {...@code HierarchicalConfigurationSource}
+ */
+ protected HierarchicalConfigurationSource<ConfigurationNode>
getTransformedSource()
+ {
+ return getTransformedConfiguration().getConfigurationSource();
+ }
+
+ /**
+ * Invalidates the data in the transformed source. Calling this method
+ * causes the configuration used internally for the transformation to be
+ * reseted, so it has to be re-constructed when the next data access
+ * happens. It can be called, for instance, if a change of the original
+ * source is detected.
+ */
+ protected synchronized void invalidate()
+ {
+ transformedConfig = null;
+ }
+
+ /**
+ * Helper method for copying the data of a configuration source into a
+ * configuration.
+ *
+ * @param config the configuration to be filled
+ * @param source the configuration source
+ */
+ private static void doFillConfiguration(Configuration<?> config,
+ ConfigurationSource source)
+ {
+ for (Iterator<String> it = source.getKeys(); it.hasNext();)
+ {
+ String key = it.next();
+ config.addProperty(key, source.getProperty(key));
+ }
+ }
+
+ /**
+ * Helper method for copying the data of a configuration into a
+ * configuration source.
+ *
+ * @param source the configuration source
+ * @param config the configuration
+ */
+ private static void doFillSource(ConfigurationSource source,
+ Configuration<?> config)
+ {
+ for (Iterator<String> it = config.getKeys(); it.hasNext();)
+ {
+ String key = it.next();
+ source.addProperty(key, config.getProperty(key));
+ }
+ }
+}
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/HierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java?rev=804523&view=auto
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
(added)
+++
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
Sat Aug 15 20:13:06 2009
@@ -0,0 +1,580 @@
+/*
+ * 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.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.configuration2.expr.ConfigurationNodeHandler;
+import org.apache.commons.configuration2.expr.NodeHandler;
+import org.apache.commons.configuration2.tree.ConfigurationNode;
+import org.apache.commons.lang.mutable.MutableObject;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+
+/**
+ * Test class for {...@code HierarchicalSourceAdapter}.
+ *
+ * @author Commons Configuration team
+ * @version $Id$
+ */
+public class TestHierarchicalSourceAdapter extends TestCase
+{
+ /** An array with the names of the test properties. */
+ private static final String[] KEYS = {
+ "db.user", "db.pwd", "db.driver", "gui.color.fg", "gui.color.bg",
+ "gui.width", "gui.height", "test"
+ };
+
+ /** An array with the values of the test properties. */
+ private static final Object[] VALUES = {
+ "scott", "elephant", "test.driver", "pink", "black", 320, 200, true
+ };
+
+ /** Constant for the key of a new property. */
+ private static final String NEW_KEY = "test2";
+
+ /** Constant for the value of the new property. */
+ private static final Object NEW_VALUE = Boolean.TRUE;
+
+ /**
+ * Helper method for creating a mock for a hierarchical source.
+ *
+ * @return the mock
+ */
+ private static HierarchicalConfigurationSource<ConfigurationNode>
createHierarchicalSourceMock()
+ {
+ @SuppressWarnings("unchecked")
+ HierarchicalConfigurationSource<ConfigurationNode> mock = EasyMock
+ .createMock(HierarchicalConfigurationSource.class);
+ return mock;
+ }
+
+ /**
+ * Helper method for creating a mock for a configuration.
+ *
+ * @return the mock
+ */
+ private static Configuration<ConfigurationNode> createConfigurationMock()
+ {
+ @SuppressWarnings("unchecked")
+ Configuration<ConfigurationNode> mock = EasyMock
+ .createMock(Configuration.class);
+ return mock;
+ }
+
+ /**
+ * Prepares a mock source object to be queried for its properties for a
+ * transformation. This method prepares the mock to expect a number of
+ * getProperty() calls for all test properties.
+ *
+ * @param src the mock for the source
+ */
+ private static void prepareTransformPropertyValues(ConfigurationSource src)
+ {
+ for (int i = 0; i < KEYS.length; i++)
+ {
+ EasyMock.expect(src.getProperty(KEYS[i])).andReturn(VALUES[i]);
+ }
+ }
+
+ /**
+ * Prepares a mock source object for a writeBack() operation. The mock is
+ * initialized to expect addProperty() for all test properties and the new
+ * property.
+ *
+ * @param src the mock for the source
+ * @param clear a flag whether the clear() call should be expected, too
+ */
+ private static void prepareWriteBack(ConfigurationSource src, boolean
clear)
+ {
+ if (clear)
+ {
+ src.clear();
+ }
+ for (int i = 0; i < KEYS.length; i++)
+ {
+ src.addProperty(KEYS[i], VALUES[i]);
+ }
+ src.addProperty(NEW_KEY, NEW_VALUE);
+ }
+
+ /**
+ * Tries to create an instance without a wrapped source. This should cause
+ * an exception.
+ */
+ public void testInitNoWrappedSource()
+ {
+ try
+ {
+ new HierarchicalSourceAdapter(null);
+ fail("Could create instance with null wrapped source!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ // ok
+ }
+ }
+
+ /**
+ * Tests the constructor that takes only the wrapped source.
+ */
+ public void testInit()
+ {
+ ConfigurationSource src = EasyMock
+ .createMock(ConfigurationSource.class);
+ EasyMock.replay(src);
+ HierarchicalSourceAdapter adapter = new HierarchicalSourceAdapter(src);
+ assertEquals("Wrong wrapped source", src, adapter.getOriginalSource());
+ assertFalse("Wrong monitor flag", adapter.isMonitorChanges());
+ EasyMock.verify(src);
+ }
+
+ /**
+ * Tests the constructor that enables monitoring changes.
+ */
+ public void testInitMonitor()
+ {
+ ConfigurationSource src = EasyMock
+ .createMock(ConfigurationSource.class);
+ EasyMock.replay(src);
+ final MutableObject listener = new MutableObject();
+ ConfigurationSource orgSrc = new ConfigurationSourceEventWrapper(src)
+ {
+ @Override
+ public void addConfigurationSourceListener(
+ ConfigurationSourceListener l)
+ {
+ listener.setValue(l);
+ }
+ };
+ HierarchicalSourceAdapter adapter = new HierarchicalSourceAdapter(
+ orgSrc, true);
+ assertTrue("Wrong monitor flag", adapter.isMonitorChanges());
+ assertEquals("Listener not registered", adapter, listener.getValue());
+ }
+
+ /**
+ * Tests the clear() implementation.
+ */
+ public void testClear()
+ {
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ adapter.mockSource = createHierarchicalSourceMock();
+ adapter.mockSource.clear();
+ EasyMock.replay(adapter.mockSource, adapter.getOriginalSource());
+ adapter.clear();
+ EasyMock.verify(adapter.mockSource, adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests whether configuration source listener can be registered at the
+ * transformed source.
+ */
+ public void testAddConfigurationSourceListener()
+ {
+ ConfigurationSourceListener l = EasyMock
+ .createMock(ConfigurationSourceListener.class);
+ HierarchicalConfigurationSource<ConfigurationNode> src =
createHierarchicalSourceMock();
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ src.addConfigurationSourceListener(l);
+ EasyMock.replay(l, src);
+ adapter.mockSource = src;
+ adapter.addConfigurationSourceListener(l);
+ EasyMock.verify(l, src);
+ }
+
+ /**
+ * Tests whether a configuration source listener can be removed from the
+ * transformed source.
+ */
+ public void testRemoveConfigurationSourceListener()
+ {
+ ConfigurationSourceListener l = EasyMock
+ .createMock(ConfigurationSourceListener.class);
+ HierarchicalConfigurationSource<ConfigurationNode> src =
createHierarchicalSourceMock();
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ EasyMock.expect(src.removeConfigurationSourceListener(l)).andReturn(
+ Boolean.TRUE);
+ EasyMock.replay(l, src);
+ adapter.mockSource = src;
+ assertTrue("Wrong result",
adapter.removeConfigurationSourceListener(l));
+ EasyMock.verify(l, src);
+ }
+
+ /**
+ * Tests whether the correct root node is returned.
+ */
+ public void testGetRootNode()
+ {
+ HierarchicalConfigurationSource<ConfigurationNode> src =
createHierarchicalSourceMock();
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ ConfigurationNode root = EasyMock.createMock(ConfigurationNode.class);
+ EasyMock.expect(src.getRootNode()).andReturn(root);
+ EasyMock.replay(src, root, adapter.getOriginalSource());
+ adapter.mockSource = src;
+ assertEquals("Wrong root node", root, adapter.getRootNode());
+ EasyMock.verify(src, root, adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests whether a new root node can be set.
+ */
+ public void testSetRootNode()
+ {
+ HierarchicalConfigurationSource<ConfigurationNode> src =
createHierarchicalSourceMock();
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ ConfigurationNode root = EasyMock.createMock(ConfigurationNode.class);
+ src.setRootNode(root);
+ EasyMock.replay(src, root, adapter.getOriginalSource());
+ adapter.mockSource = src;
+ adapter.setRootNode(root);
+ EasyMock.verify(src, root, adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests whether the correct node handler is returned.
+ */
+ public void testGetNodeHandler()
+ {
+ HierarchicalConfigurationSource<ConfigurationNode> src =
createHierarchicalSourceMock();
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ NodeHandler<ConfigurationNode> handler = new
ConfigurationNodeHandler();
+ EasyMock.expect(src.getNodeHandler()).andReturn(handler);
+ EasyMock.replay(src, adapter.getOriginalSource());
+ adapter.mockSource = src;
+ assertEquals("Wrong node handler", handler, adapter.getNodeHandler());
+ EasyMock.verify(src, adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests whether the given configuration contains the expected data.
+ *
+ * @param config the configuration to check
+ */
+ private void checkTransformedConfiguration(
+ Configuration<ConfigurationNode> config)
+ {
+ Set<String> keySet = new HashSet<String>(Arrays.asList(KEYS));
+ for (Iterator<String> it = config.getKeys(); it.hasNext();)
+ {
+ String key = it.next();
+ assertTrue("Unexpected key: " + key, keySet.remove(key));
+ }
+ assertTrue("Remaining keys: " + keySet, keySet.isEmpty());
+ for (int i = 0; i < KEYS.length; i++)
+ {
+ assertEquals("Wrong value for " + KEYS[i], VALUES[i], config
+ .getProperty(KEYS[i]));
+ }
+ Configuration<ConfigurationNode> sub = config.configurationAt("gui");
+ assertEquals("Wrong sub property 1", 320, sub.getInt("width"));
+ assertEquals("Wrong sub property 2", "black",
sub.getString("color.bg"));
+ }
+
+ /**
+ * Tests whether the plain configuration source is transformed into a
+ * hierarchical one.
+ */
+ public void testTransformation()
+ {
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ prepareTransformPropertyValues(adapter.getOriginalSource());
+ EasyMock.expect(adapter.getOriginalSource().getKeys()).andReturn(
+ Arrays.asList(KEYS).iterator());
+ EasyMock.replay(adapter.getOriginalSource());
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ adapter);
+ checkTransformedConfiguration(config);
+ EasyMock.verify(adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests whether changes of the original source are monitored and whether
+ * the transformed source is re-constructed if a change is noticed.
+ */
+ public void testTransformationMonitor()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ source
+ .addConfigurationSourceListener((ConfigurationSourceListener)
EasyMock
+ .anyObject());
+ prepareTransformPropertyValues(source);
+ EasyMock.expect(source.getKeys()).andReturn(
+ Arrays.asList(KEYS).iterator());
+ prepareTransformPropertyValues(source);
+ EasyMock.expect(source.getProperty(NEW_KEY)).andReturn(NEW_VALUE);
+ List<String> keyList = new ArrayList<String>(Arrays.asList(KEYS));
+ keyList.add(NEW_KEY);
+ EasyMock.expect(source.getKeys()).andReturn(keyList.iterator());
+ EasyMock.replay(source);
+ HierarchicalSourceAdapter adapter = new HierarchicalSourceAdapter(
+ source, true);
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ adapter);
+ assertFalse("New key already found", config.containsKey(NEW_KEY));
+ // simulate a change event
+ adapter.configurationSourceChanged(new ConfigurationSourceEvent(source,
+ ConfigurationSourceEvent.Type.ADD_PROPERTY, NEW_KEY, NEW_VALUE,
+ null, true));
+ adapter.configurationSourceChanged(new ConfigurationSourceEvent(source,
+ ConfigurationSourceEvent.Type.ADD_PROPERTY, NEW_KEY, NEW_VALUE,
+ null, false));
+ assertEquals("Wrong value of new property", NEW_VALUE, config
+ .getProperty(NEW_KEY));
+ assertEquals("Wrong value of old property", 320, config
+ .getInt("gui.width"));
+ EasyMock.verify(source);
+ }
+
+ /**
+ * Tests whether data stored in the adapter can be written back into the
+ * original source.
+ */
+ public void testWriteBack()
+ {
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl();
+ EasyMock.expect(adapter.getOriginalSource().getKeys()).andReturn(
+ Arrays.asList(KEYS).iterator());
+ prepareTransformPropertyValues(adapter.getOriginalSource());
+ prepareWriteBack(adapter.getOriginalSource(), true);
+ EasyMock.replay(adapter.getOriginalSource());
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ adapter);
+ assertFalse("Configuration is empty", config.isEmpty());
+ config.addProperty(NEW_KEY, NEW_VALUE);
+ adapter.writeBack();
+ EasyMock.verify(adapter.getOriginalSource());
+ }
+
+ /**
+ * Tests the writeBack() method if monitoring of the original source is
+ * enabled. In this case the adapter should de-register itself before it
+ * changes the original source.
+ */
+ public void testWriteBackMonitor()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ final MutableObject expectedListener = new MutableObject();
+ source
+ .addConfigurationSourceListener((ConfigurationSourceListener)
EasyMock
+ .anyObject());
+ EasyMock.expect(source.getKeys()).andReturn(
+ Arrays.asList(KEYS).iterator());
+ prepareTransformPropertyValues(source);
+ EasyMock
+ .expect(
+ source
+
.removeConfigurationSourceListener((ConfigurationSourceListener) EasyMock
+ .anyObject())).andAnswer(
+ new IAnswer<Boolean>()
+ {
+ /* Check whether the expected listener is passed
in.*/
+ public Boolean answer() throws Throwable
+ {
+ assertEquals("Wrong event listener to remove",
+ expectedListener.getValue(), EasyMock
+ .getCurrentArguments()[0]);
+ return Boolean.TRUE;
+ }
+ });
+ prepareWriteBack(source, true);
+ source
+ .addConfigurationSourceListener((ConfigurationSourceListener)
EasyMock
+ .anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>()
+ {
+ /* Check whether the expected listener is passed in.*/
+ public Object answer() throws Throwable
+ {
+ assertEquals("Wrong event listener to add", expectedListener
+ .getValue(), EasyMock.getCurrentArguments()[0]);
+ return null;
+ }
+ });
+ EasyMock.replay(source);
+ HierarchicalSourceAdapterTestImpl adapter = new
HierarchicalSourceAdapterTestImpl(
+ source, true);
+ expectedListener.setValue(adapter);
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ adapter);
+ assertFalse("Configuration is empty", config.isEmpty());
+ config.addProperty(NEW_KEY, NEW_VALUE);
+ adapter.writeBack();
+ EasyMock.verify(source);
+ }
+
+ /**
+ * Tests the utility method for copying a source into a configuration.
+ */
+ public void testFillConfiguration()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ EasyMock.expect(source.getKeys()).andReturn(
+ Arrays.asList(KEYS).iterator());
+ prepareTransformPropertyValues(source);
+ EasyMock.replay(source);
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ new InMemoryConfigurationSource());
+ HierarchicalSourceAdapter.fillConfiguration(config, source);
+ checkTransformedConfiguration(config);
+ EasyMock.verify(source);
+ }
+
+ /**
+ * Tries to call fillConfiguration() with a null configuration. This should
+ * cause an exception.
+ */
+ public void testFillConfigurationNullConfig()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ EasyMock.replay(source);
+ try
+ {
+ HierarchicalSourceAdapter.fillConfiguration(null, source);
+ fail("Null configuration not detected!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ EasyMock.verify(source);
+ }
+ }
+
+ /**
+ * Tries to call fillConfiguration() with a null source. This should cause
+ * an exception.
+ */
+ public void testFillConfigurationNullSource()
+ {
+ Configuration<ConfigurationNode> config = createConfigurationMock();
+ EasyMock.replay(config);
+ try
+ {
+ HierarchicalSourceAdapter.fillConfiguration(config, null);
+ fail("Null source not detected!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ EasyMock.verify(config);
+ }
+ }
+
+ /**
+ * Tests the utility method for copying a configuration into a source.
+ */
+ public void testFillSource()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ prepareWriteBack(source, false);
+ EasyMock.replay(source);
+ Configuration<ConfigurationNode> config = new
ConfigurationImpl<ConfigurationNode>(
+ new InMemoryConfigurationSource());
+ for (int i = 0; i < KEYS.length; i++)
+ {
+ config.addProperty(KEYS[i], VALUES[i]);
+ }
+ config.addProperty(NEW_KEY, NEW_VALUE);
+ HierarchicalSourceAdapter.fillSource(source, config);
+ EasyMock.verify(source);
+ }
+
+ /**
+ * Tries to call fillSource() with a null configuration. This should cause
+ * an exception.
+ */
+ public void testFillSourceNullConfig()
+ {
+ ConfigurationSource source = EasyMock
+ .createMock(ConfigurationSource.class);
+ EasyMock.replay(source);
+ try
+ {
+ HierarchicalSourceAdapter.fillSource(source, null);
+ fail("Null configuration not detected!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ EasyMock.verify(source);
+ }
+ }
+
+ /**
+ * Tries to call fillSource() with a null source. This should cause an
+ * exception.
+ */
+ public void testFillSourceNullSource()
+ {
+ Configuration<ConfigurationNode> config = createConfigurationMock();
+ EasyMock.replay(config);
+ try
+ {
+ HierarchicalSourceAdapter.fillSource(null, config);
+ fail("Null source not detected!");
+ }
+ catch (IllegalArgumentException iex)
+ {
+ EasyMock.verify(config);
+ }
+ }
+
+ /**
+ * A specialized implementation of {...@code HierarchicalSourceAdapter}
that
+ * overrides some methods to inject mock objects.
+ */
+ private static class HierarchicalSourceAdapterTestImpl extends
+ HierarchicalSourceAdapter
+ {
+ /** The mock transformed source. */
+ HierarchicalConfigurationSource<ConfigurationNode> mockSource;
+
+ public HierarchicalSourceAdapterTestImpl(
+ ConfigurationSource wrappedSource, boolean monitorChanges)
+ {
+ super(wrappedSource, monitorChanges);
+ }
+
+ /**
+ * Creates a new instance of {...@code
HierarchicalSourceAdapterTestImpl}
+ * and sets a default mock object for the wrapped source.
+ */
+ public HierarchicalSourceAdapterTestImpl()
+ {
+ super(EasyMock.createMock(ConfigurationSource.class));
+ }
+
+ /**
+ * Either returns the mock source or calls the super method.
+ */
+ @Override
+ protected HierarchicalConfigurationSource<ConfigurationNode>
getTransformedSource()
+ {
+ return (mockSource != null) ? mockSource : super
+ .getTransformedSource();
+ }
+ }
+}
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/TestHierarchicalSourceAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain