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 2013/04/12 22:24:03 UTC

svn commit: r1467445 - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/ test/java/org/apache/commons/configuration/

Author: oheger
Date: Fri Apr 12 20:24:02 2013
New Revision: 1467445

URL: http://svn.apache.org/r1467445
Log:
[CONFIGURATION-524] Added support for cloning a ConfigurationInterpolator.

If a configuration is cloned, its interpolator has to be cloned, too, because
it typically contains a Lookup pointing to the original configuration.
AbstractConfiguration now provides default functionality for this purpose
which can be called by clone() implementations in derived classes.
BaseConfiguration already calls this method.

Modified:
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
    commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/BaseConfiguration.java
    commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.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=1467445&r1=1467444&r2=1467445&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 Fri Apr 12 20:24:02 2013
@@ -145,7 +145,7 @@ public abstract class AbstractConfigurat
     private boolean throwExceptionOnMissing;
 
     /** Stores a reference to the object that handles variable interpolation. */
-    private final AtomicReference<ConfigurationInterpolator> interpolator;
+    private AtomicReference<ConfigurationInterpolator> interpolator;
 
     /** Stores the logger.*/
     private Log log;
@@ -426,6 +426,39 @@ public abstract class AbstractConfigurat
     }
 
     /**
+     * Creates a clone of the {@code ConfigurationInterpolator} used by this
+     * instance. This method can be called by {@code clone()} implementations of
+     * derived classes. Normally, the {@code ConfigurationInterpolator} of a
+     * configuration instance must not be shared with other instances because it
+     * contains a specific {@code Lookup} object pointing to the owning
+     * configuration. This has to be taken into account when cloning a
+     * configuration. This method creates a new
+     * {@code ConfigurationInterpolator} for this configuration instance which
+     * contains all lookup objects from the original
+     * {@code ConfigurationInterpolator} except for the configuration specific
+     * lookup pointing to the passed in original configuration. This one is
+     * replaced by a corresponding {@code Lookup} referring to this
+     * configuration.
+     *
+     * @param orgConfig the original configuration from which this one was
+     *        cloned
+     * @since 2.0
+     */
+    protected void cloneInterpolator(AbstractConfiguration orgConfig)
+    {
+        interpolator = new AtomicReference<ConfigurationInterpolator>();
+        ConfigurationInterpolator orgInterpolator = orgConfig.getInterpolator();
+        List<Lookup> defaultLookups = orgInterpolator.getDefaultLookups();
+        Lookup lookup = findConfigurationLookup(orgInterpolator, orgConfig);
+        if (lookup != null)
+        {
+            defaultLookups.remove(lookup);
+        }
+
+        installInterpolator(orgInterpolator.getLookups(), defaultLookups);
+    }
+
+    /**
      * 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
@@ -448,11 +481,26 @@ public abstract class AbstractConfigurat
      */
     private Lookup findConfigurationLookup(ConfigurationInterpolator ci)
     {
+        return findConfigurationLookup(ci, this);
+    }
+
+    /**
+     * Finds a {@code ConfigurationLookup} pointing to the specified
+     * configuration in the default lookups for the specified
+     * {@code ConfigurationInterpolator}.
+     *
+     * @param ci the {@code ConfigurationInterpolator} in question
+     * @param targetConf the target configuration of the searched lookup
+     * @return the found {@code Lookup} object or <b>null</b>
+     */
+    private static Lookup findConfigurationLookup(ConfigurationInterpolator ci,
+            ImmutableConfiguration targetConf)
+    {
         for (Lookup l : ci.getDefaultLookups())
         {
             if (l instanceof ConfigurationLookup)
             {
-                if (this == ((ConfigurationLookup) l).getConfiguration())
+                if (targetConf == ((ConfigurationLookup) l).getConfiguration())
                 {
                     return l;
                 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/BaseConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/BaseConfiguration.java?rev=1467445&r1=1467444&r2=1467445&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/BaseConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/BaseConfiguration.java Fri Apr 12 20:24:02 2013
@@ -170,22 +170,8 @@ public class BaseConfiguration extends A
         try
         {
             BaseConfiguration copy = (BaseConfiguration) super.clone();
-            // This is safe because the type of the map is known
-            @SuppressWarnings("unchecked")
-            Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store);
-            copy.store = clonedStore;
-
-            // Handle collections in the map; they have to be cloned, too
-            for (Map.Entry<String, Object> e : store.entrySet())
-            {
-                if (e.getValue() instanceof Collection)
-                {
-                    // This is safe because the collections were created by ourselves
-                    @SuppressWarnings("unchecked")
-                    Collection<String> strList = (Collection<String>) e.getValue();
-                    copy.store.put(e.getKey(), new ArrayList<String>(strList));
-                }
-            }
+            cloneStore(copy);
+            copy.cloneInterpolator(this);
 
             return copy;
         }
@@ -195,4 +181,31 @@ public class BaseConfiguration extends A
             throw new ConfigurationRuntimeException(cex);
         }
     }
+
+    /**
+     * Clones the internal map with the data of this configuration.
+     *
+     * @param copy the copy created by the {@code clone()} method
+     * @throws CloneNotSupportedException if the map cannot be cloned
+     */
+    private void cloneStore(BaseConfiguration copy)
+            throws CloneNotSupportedException
+    {
+        // This is safe because the type of the map is known
+        @SuppressWarnings("unchecked")
+        Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store);
+        copy.store = clonedStore;
+
+        // Handle collections in the map; they have to be cloned, too
+        for (Map.Entry<String, Object> e : store.entrySet())
+        {
+            if (e.getValue() instanceof Collection)
+            {
+                // This is safe because the collections were created by ourselves
+                @SuppressWarnings("unchecked")
+                Collection<String> strList = (Collection<String>) e.getValue();
+                copy.store.put(e.getKey(), new ArrayList<String>(strList));
+            }
+        }
+    }
 }

Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java?rev=1467445&r1=1467444&r2=1467445&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java Fri Apr 12 20:24:02 2013
@@ -800,4 +800,21 @@ public class TestBaseConfiguration
         assertEquals("Wrong number of original properties", 2, config.getList(
                 key).size());
     }
+
+    /**
+     * Tests whether interpolation works as expected after cloning.
+     */
+    @Test
+    public void testCloneInterpolation()
+    {
+        final String keyAnswer = "answer";
+        config.addProperty(keyAnswer, "The answer is ${" + KEY_NUMBER + "}.");
+        config.addProperty(KEY_NUMBER, 42);
+        BaseConfiguration clone = (BaseConfiguration) config.clone();
+        clone.setProperty(KEY_NUMBER, 43);
+        assertEquals("Wrong interpolation in original", "The answer is 42.",
+                config.getString(keyAnswer));
+        assertEquals("Wrong interpolation in clone", "The answer is 43.",
+                clone.getString(keyAnswer));
+    }
 }