Author: oheger Date: Wed Dec 26 15:52:27 2012 New Revision: 1425960 URL: http://svn.apache.org/viewvc?rev=1425960&view=rev Log: [CONFIGURATION-518] Added set methods to AbstractConfiguration to manipulate the Lookup objects of the current ConfigurationInterpolator.
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java?rev=1425960&r1=1425959&r2=1425960&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java Wed Dec 26 15:52:27 2012 @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.configuration.event.BaseEventSource; import org.apache.commons.configuration.event.ConfigurationErrorEvent; @@ -145,7 +146,7 @@ public abstract class AbstractConfigurat private boolean throwExceptionOnMissing; /** Stores a reference to the object that handles variable interpolation. */ - private volatile ConfigurationInterpolator interpolator; + private final AtomicReference<ConfigurationInterpolator> interpolator; /** Stores the logger.*/ private Log log; @@ -155,6 +156,7 @@ public abstract class AbstractConfigurat */ public AbstractConfiguration() { + interpolator = new AtomicReference<ConfigurationInterpolator>(); setLogger(null); installDefaultInterpolator(); } @@ -294,7 +296,7 @@ public abstract class AbstractConfigurat */ public ConfigurationInterpolator getInterpolator() { - return interpolator; + return interpolator.get(); } /** @@ -306,7 +308,7 @@ public abstract class AbstractConfigurat */ public final void setInterpolator(ConfigurationInterpolator ci) { - interpolator = ci; + interpolator.set(ci); } /** @@ -329,6 +331,74 @@ public abstract class AbstractConfigurat } /** + * Registers all {@code Lookup} objects in the given map at the current + * {@code ConfigurationInterpolator} of this configuration. The set of + * default lookup objects (for variables without a prefix) is not modified + * by this method. If this configuration does not have a + * {@code ConfigurationInterpolator}, a new instance is created. Note: This + * method is mainly intended to be used for initializing a configuration + * when it is created by a builder. Normal client code should better call + * {@link #installInterpolator(Map, Collection)} to define the + * {@code ConfigurationInterpolator} in a single step. + * + * @param lookups a map with new {@code Lookup} objects and their prefixes + * (may be <b>null</b>) + */ + public final void setPrefixLookups(Map<String, ? extends Lookup> lookups) + { + boolean success; + do + { + // do this in a loop because the ConfigurationInterpolator + // instance may be changed by another thread + ConfigurationInterpolator ciOld = getInterpolator(); + ConfigurationInterpolator ciNew = + (ciOld != null) ? ciOld : new ConfigurationInterpolator(); + ciNew.registerLookups(lookups); + success = interpolator.compareAndSet(ciOld, ciNew); + } while (!success); + } + + /** + * Adds all {@code Lookup} objects in the given collection as default + * lookups (i.e. lookups without a variable prefix) to the + * {@code ConfigurationInterpolator} object of this configuration. In + * addition, it adds a specialized default {@code Lookup} object which + * queries this {@code Configuration}. The set of {@code Lookup} objects + * with prefixes is not modified by this method. If this configuration does + * not have a {@code ConfigurationInterpolator}, a new instance is created. + * Note: This method is mainly intended to be used for initializing a + * configuration when it is created by a builder. Normal client code should + * better call {@link #installInterpolator(Map, Collection)} to define the + * {@code ConfigurationInterpolator} in a single step. + * + * @param lookups the collection with default {@code Lookup} objects to be + * added + */ + public final void setDefaultLookups(Collection<? extends Lookup> lookups) + { + boolean success; + do + { + ConfigurationInterpolator ciOld = getInterpolator(); + ConfigurationInterpolator ciNew = + (ciOld != null) ? ciOld : new ConfigurationInterpolator(); + Lookup confLookup = findConfigurationLookup(ciNew); + if (confLookup == null) + { + confLookup = new ConfigurationLookup(this); + } + else + { + ciNew.removeDefaultLookup(confLookup); + } + ciNew.addDefaultLookups(lookups); + ciNew.addDefaultLookup(confLookup); + success = interpolator.compareAndSet(ciOld, ciNew); + } while (!success); + } + + /** * Creates a default {@code ConfigurationInterpolator} which is initialized * with all default {@code Lookup} objects. This method is called by the * constructor. It ensures that default interpolation works for every new @@ -345,6 +415,30 @@ public abstract class AbstractConfigurat } /** + * Finds a {@code ConfigurationLookup} pointing to this configuration in the + * default lookups of the specified {@code ConfigurationInterpolator}. This + * method is called to ensure that there is exactly one default lookup + * querying this configuration. + * + * @param ci the {@code ConfigurationInterpolator} in question + * @return the found {@code Lookup} object or <b>null</b> + */ + private Lookup findConfigurationLookup(ConfigurationInterpolator ci) + { + for (Lookup l : ci.getDefaultLookups()) + { + if (l instanceof ConfigurationLookup) + { + if (this == ((ConfigurationLookup) l).getConfiguration()) + { + return l; + } + } + } + return null; + } + + /** * Returns the logger used by this configuration object. * * @return the logger Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java?rev=1425960&r1=1425959&r2=1425960&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java (original) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java Wed Dec 26 15:52:27 2012 @@ -17,11 +17,13 @@ package org.apache.commons.configuration; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -30,6 +32,8 @@ import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.event.ConfigurationEvent; import org.apache.commons.configuration.event.ConfigurationListener; +import org.apache.commons.configuration.interpol.Lookup; +import org.easymock.EasyMock; import org.junit.Test; /** @@ -281,6 +285,103 @@ public class TestAbstractConfigurationBa } /** + * Tests whether prefix lookups can be added to an existing + * {@code ConfigurationInterpolator}. + */ + @Test + public void testSetPrefixLookupsExistingInterpolator() + { + Lookup look = EasyMock.createMock(Lookup.class); + EasyMock.replay(look); + AbstractConfiguration config = + new TestConfigurationImpl(new PropertiesConfiguration()); + int count = config.getInterpolator().getLookups().size(); + Map<String, Lookup> lookups = new HashMap<String, Lookup>(); + lookups.put("test", look); + config.setPrefixLookups(lookups); + Map<String, Lookup> lookups2 = config.getInterpolator().getLookups(); + assertEquals("Not added", count + 1, lookups2.size()); + assertSame("Not found", look, lookups2.get("test")); + } + + /** + * Tests whether prefix lookups can be added if no + * {@code ConfigurationInterpolator} exists yet. + */ + @Test + public void testSetPrefixLookupsNoInterpolator() + { + Lookup look = EasyMock.createMock(Lookup.class); + EasyMock.replay(look); + AbstractConfiguration config = + new TestConfigurationImpl(new PropertiesConfiguration()); + config.setInterpolator(null); + config.setPrefixLookups(Collections.singletonMap("test", look)); + Map<String, Lookup> lookups = config.getInterpolator().getLookups(); + assertEquals("Wrong number of lookups", 1, lookups.size()); + assertSame("Not found", look, lookups.get("test")); + } + + /** + * Tests whether default lookups can be added to an already existing + * {@code ConfigurationInterpolator}. + */ + @Test + public void testSetDefaultLookupsExistingInterpolator() + { + Lookup look = EasyMock.createMock(Lookup.class); + EasyMock.replay(look); + AbstractConfiguration config = + new TestConfigurationImpl(new PropertiesConfiguration()); + config.getInterpolator().addDefaultLookup( + new ConfigurationLookup(new PropertiesConfiguration())); + config.setDefaultLookups(Collections.singleton(look)); + List<Lookup> lookups = config.getInterpolator().getDefaultLookups(); + assertEquals("Wrong number of default lookups", 3, lookups.size()); + assertSame("Wrong lookup at 1", look, lookups.get(1)); + assertTrue("Wrong lookup at 2: " + lookups, + lookups.get(2) instanceof ConfigurationLookup); + } + + /** + * Tests whether default lookups can be added if not + * {@code ConfigurationInterpolator} exists yet. + */ + @Test + public void testSetDefaultLookupsNoInterpolator() + { + Lookup look = EasyMock.createMock(Lookup.class); + EasyMock.replay(look); + AbstractConfiguration config = + new TestConfigurationImpl(new PropertiesConfiguration()); + config.setInterpolator(null); + config.setDefaultLookups(Collections.singleton(look)); + List<Lookup> lookups = config.getInterpolator().getDefaultLookups(); + assertEquals("Wrong number of default lookups", 2, lookups.size()); + assertSame("Wrong lookup at 0", look, lookups.get(0)); + assertTrue("Wrong lookup at 1", + lookups.get(1) instanceof ConfigurationLookup); + } + + /** + * Tests whether a new {@code ConfigurationInterpolator} can be installed + * without providing custom lookups. + */ + @Test + public void testInstallInterpolatorNull() + { + AbstractConfiguration config = + new TestConfigurationImpl(new PropertiesConfiguration()); + config.installInterpolator(null, null); + assertTrue("Got prefix lookups", config.getInterpolator().getLookups() + .isEmpty()); + List<Lookup> defLookups = config.getInterpolator().getDefaultLookups(); + assertEquals("Wrong number of default lookups", 1, defLookups.size()); + assertTrue("Wrong default lookup", + defLookups.get(0) instanceof ConfigurationLookup); + } + + /** * Tests getList() for single non-string values. */ @Test