    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.configuration;
    18
    19  import java.util.ArrayList;
    20  import java.util.Arrays;
    21  import java.util.Collection;
    22  import java.util.HashMap;
    23  import java.util.Iterator;
    24  import java.util.List;
    25  import java.util.Map;
    26
    27  import org.apache.commons.collections.CollectionUtils;
    28  import org.apache.commons.configuration.event.ConfigurationEvent;
    29  import org.apache.commons.configuration.event.ConfigurationListener;
    30
    31  import junit.framework.TestCase;
    32
    33  /***
    34   * A test class for some of the basic functionality implemented by
    35   * AbstractConfiguration.
    36   *
    37   * @version $Id: TestAbstractConfigurationBasicFeatures.java 567771 2007-08-20 17:57:08Z oheger $
    38   */
    39  public class TestAbstractConfigurationBasicFeatures extends TestCase
    40  {
    41      /*** Constant for the prefix of test keys.*/
    42      private static final String KEY_PREFIX = "key";
    43
    44      /*** Constant for the number of properties in tests for copy operations.*/
    45      private static final int PROP_COUNT = 12;
    46
    47      /***
    48       * Tests the clear() implementation of AbstractConfiguration if the iterator
    49       * returned by getKeys() does not support the remove() operation.
    50       */
    51      public void testClearIteratorNoRemove()
    52      {
    53          AbstractConfiguration config = new TestConfigurationImpl(
    54                  new BaseConfiguration())
    55          {
    56              // return an iterator that does not support remove operations
    57              public Iterator getKeys()
    58              {
    59                  Collection keyCol = new ArrayList();
    60                  CollectionUtils.addAll(keyCol, getUnderlyingConfiguration()
    61                          .getKeys());
    62                  Object[] keys = keyCol.toArray();
    63                  return Arrays.asList(keys).iterator();
    64              }
    65          };
    66          for (int i = 0; i < 20; i++)
    67          {
    68              config.addProperty("key" + i, "value" + i);
    69          }
    70          config.clear();
    71          assertTrue("Configuration not empty", config.isEmpty());
    72      }
    73
    74      /***
    75       * Tests escaping the variable marker, so that no interpolation will be
    76       * performed.
    77       */
    78      public void testInterpolateEscape()
    79      {
    80          AbstractConfiguration config = new TestConfigurationImpl(
    81                  new PropertiesConfiguration());
    82          config
    83                  .addProperty(
    84                          "mypath",
    85                          "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar//,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
    86          assertEquals(
    87                  "Wrong interpolated value",
    88                  "${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar",
    89                  config.getString("mypath"));
    90      }
    91
    92      /***
    93       * Tests adding list properties. The single elements of the list should be
    94       * added.
    95       */
    96      public void testAddPropertyList()
    97      {
    98          checkAddListProperty(new TestConfigurationImpl(
    99                  new PropertiesConfiguration()));
    100     }
    101
    102     /***
    103      * Tests adding list properties when delimiter parsing is disabled.
    104      */
    105     public void testAddPropertyListNoDelimiterParsing()
    106     {
    107         AbstractConfiguration config = new TestConfigurationImpl(
    108                 new PropertiesConfiguration());
    109         config.setDelimiterParsingDisabled(true);
    110         checkAddListProperty(config);
    111     }
    112
    113     /***
    114      * Helper method for adding properties with multiple values.
    115      *
    116      * @param config the configuration to be used for testing
    117      */
    118     private void checkAddListProperty(AbstractConfiguration config)
    119     {
    120         config.addProperty("test", "value1");
    121         Object[] lstValues1 = new Object[]
    122         { "value2", "value3" };
    123         Object[] lstValues2 = new Object[]
    124         { "value4", "value5", "value6" };
    125         config.addProperty("test", lstValues1);
    126         config.addProperty("test", Arrays.asList(lstValues2));
    127         List lst = config.getList("test");
    128         assertEquals("Wrong number of list elements", 6, lst.size());
    129         for (int i = 0; i < lst.size(); i++)
    130         {
    131             assertEquals("Wrong list element at " + i, "value" + (i + 1), lst
    132                     .get(i));
    133         }
    134     }
    135
    136     /***
    137      * Tests the copy() method.
    138      */
    139     public void testCopy()
    140     {
    141         AbstractConfiguration config = setUpDestConfig();
    142         Configuration srcConfig = setUpSourceConfig();
    143         config.copy(srcConfig);
    144         for (int i = 0; i < PROP_COUNT; i++)
    145         {
    146             String key = KEY_PREFIX + i;
    147             if (srcConfig.containsKey(key))
    148             {
    149                 assertEquals("Value not replaced: " + key, srcConfig
    150                         .getProperty(key), config.getProperty(key));
    151             }
    152             else
    153             {
    154                 assertEquals("Value modified: " + key, "value" + i, config
    155                         .getProperty(key));
    156             }
    157         }
    158     }
    159
    160     /***
    161      * Tests the copy() method when properties with multiple values and escaped
    162      * list delimiters are involved.
    163      */
    164     public void testCopyWithLists()
    165     {
    166         Configuration srcConfig = setUpSourceConfig();
    167         AbstractConfiguration config = setUpDestConfig();
    168         config.copy(srcConfig);
    169         checkListProperties(config);
    170     }
    171
    172     /***
    173      * Tests the events generated by a copy() operation.
    174      */
    175     public void testCopyEvents()
    176     {
    177         AbstractConfiguration config = setUpDestConfig();
    178         Configuration srcConfig = setUpSourceConfig();
    179         CollectingConfigurationListener l = new CollectingConfigurationListener();
    180         config.addConfigurationListener(l);
    181         config.copy(srcConfig);
    182         checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_SET_PROPERTY);
    183     }
    184
    185     /***
    186      * Tests copying a null configuration. This should be a noop.
    187      */
    188     public void testCopyNull()
    189     {
    190         AbstractConfiguration config = setUpDestConfig();
    191         config.copy(null);
    192         ConfigurationAssert.assertEquals(setUpDestConfig(), config);
    193     }
    194
    195     /***
    196      * Tests the append() method.
    197      */
    198     public void testAppend()
    199     {
    200         AbstractConfiguration config = setUpDestConfig();
    201         Configuration srcConfig = setUpSourceConfig();
    202         config.append(srcConfig);
    203         for (int i = 0; i < PROP_COUNT; i++)
    204         {
    205             String key = KEY_PREFIX + i;
    206             if (srcConfig.containsKey(key))
    207             {
    208                 List values = config.getList(key);
    209                 assertEquals("Value not added: " + key, 2, values.size());
    210                 assertEquals("Wrong value 1 for " + key, "value" + i, values
    211                         .get(0));
    212                 assertEquals("Wrong value 2 for " + key, "src" + i, values
    213                         .get(1));
    214             }
    215             else
    216             {
    217                 assertEquals("Value modified: " + key, "value" + i, config
    218                         .getProperty(key));
    219             }
    220         }
    221     }
    222
    223     /***
    224      * Tests the append() method when properties with multiple values and
    225      * escaped list delimiters are involved.
    226      */
    227     public void testAppendWithLists()
    228     {
    229         AbstractConfiguration config = setUpDestConfig();
    230         config.append(setUpSourceConfig());
    231         checkListProperties(config);
    232     }
    233
    234     /***
    235      * Tests the events generated by an append() operation.
    236      */
    237     public void testAppendEvents()
    238     {
    239         AbstractConfiguration config = setUpDestConfig();
    240         Configuration srcConfig = setUpSourceConfig();
    241         CollectingConfigurationListener l = new CollectingConfigurationListener();
    242         config.addConfigurationListener(l);
    243         config.append(srcConfig);
    244         checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_ADD_PROPERTY);
    245     }
    246
    247     /***
    248      * Tests appending a null configuration. This should be a noop.
    249      */
    250     public void testAppendNull()
    251     {
    252         AbstractConfiguration config = setUpDestConfig();
    253         config.append(null);
    254         ConfigurationAssert.assertEquals(setUpDestConfig(), config);
    255     }
    256
    257     /***
    258      * Creates the source configuration for testing the copy() and append()
    259      * methods. This configuration contains keys with an odd index and values
    260      * starting with the prefix "src". There are also some list properties.
    261      *
    262      * @return the source configuration for copy operations
    263      */
    264     private Configuration setUpSourceConfig()
    265     {
    266         BaseConfiguration config = new BaseConfiguration();
    267         for (int i = 1; i < PROP_COUNT; i += 2)
    268         {
    269             config.addProperty(KEY_PREFIX + i, "src" + i);
    270         }
    271         config.addProperty("list1", "1,2,3");
    272         config.addProperty("list2", "3//,1415,9//,81");
    273         return config;
    274     }
    275
    276     /***
    277      * Creates the destination configuration for testing the copy() and append()
    278      * methods. This configuration contains keys with a running index and
    279      * corresponding values starting with the prefix "value".
    280      *
    281      * @return the destination configuration for copy operations
    282      */
    283     private AbstractConfiguration setUpDestConfig()
    284     {
    285         AbstractConfiguration config = new TestConfigurationImpl(
    286                 new PropertiesConfiguration());
    287         for (int i = 0; i < PROP_COUNT; i++)
    288         {
    289             config.addProperty(KEY_PREFIX + i, "value" + i);
    290         }
    291         return config;
    292     }
    293
    294     /***
    295      * Tests the values of list properties after a copy operation.
    296      *
    297      * @param config the configuration to test
    298      */
    299     private void checkListProperties(Configuration config)
    300     {
    301         List values = config.getList("list1");
    302         assertEquals("Wrong number of elements in list 1", 3, values.size());
    303         values = config.getList("list2");
    304         assertEquals("Wrong number of elements in list 2", 2, values.size());
    305         assertEquals("Wrong value 1", "3,1415", values.get(0));
    306         assertEquals("Wrong value 2", "9,81", values.get(1));
    307     }
    308
    309     /***
    310      * Tests whether the correct events are received for a copy operation.
    311      *
    312      * @param l the event listener
    313      * @param src the configuration that was copied
    314      * @param eventType the expected event type
    315      */
    316     private void checkCopyEvents(CollectingConfigurationListener l,
    317             Configuration src, int eventType)
    318     {
    319         Map events = new HashMap();
    320         for (Iterator it = l.events.iterator(); it.hasNext();)
    321         {
    322             ConfigurationEvent e = (ConfigurationEvent) it.next();
    323             assertEquals("Wrong event type", eventType, e.getType());
    324             assertTrue("Unknown property: " + e.getPropertyName(), src
    325                     .containsKey(e.getPropertyName()));
    326             assertEquals("Wrong property value for " + e.getPropertyName(), e
    327                     .getPropertyValue(), src.getProperty(e.getPropertyName()));
    328             if (!e.isBeforeUpdate())
    329             {
    330                 assertTrue("After event without before event", events
    331                         .containsKey(e.getPropertyName()));
    332             }
    333             else
    334             {
    335                 events.put(e.getPropertyName(), e);
    336             }
    337         }
    338
    339         for (Iterator it = src.getKeys(); it.hasNext();)
    340         {
    341             String key = (String) it.next();
    342             assertTrue("No event received for key " + key, events
    343                     .containsKey(key));
    344         }
    345     }
    346
    347     /***
    348      * A test configuration implementation. This implementation inherits
    349      * directly from AbstractConfiguration. For implementing the required
    350      * functionality another implementation of AbstractConfiguration is used;
    351      * all methods that need to be implemented delegate to this wrapped
    352      * configuration.
    353      */
    354     static class TestConfigurationImpl extends AbstractConfiguration
    355     {
    356         /*** Stores the underlying configuration. */
    357         private AbstractConfiguration config;
    358
    359         public AbstractConfiguration getUnderlyingConfiguration()
    360         {
    361             return config;
    362         }
    363
    364         public TestConfigurationImpl(AbstractConfiguration wrappedConfig)
    365         {
    366             config = wrappedConfig;
    367         }
    368
    369         protected void addPropertyDirect(String key, Object value)
    370         {
    371             config.addPropertyDirect(key, value);
    372         }
    373
    374         public boolean containsKey(String key)
    375         {
    376             return config.containsKey(key);
    377         }
    378
    379         public Iterator getKeys()
    380         {
    381             return config.getKeys();
    382         }
    383
    384         public Object getProperty(String key)
    385         {
    386             return config.getProperty(key);
    387         }
    388
    389         public boolean isEmpty()
    390         {
    391             return config.isEmpty();
    392         }
    393
    394         protected void clearPropertyDirect(String key)
    395         {
    396             config.clearPropertyDirect(key);
    397         }
    398     }
    399
    400     /***
    401      * An event listener implementation that simply collects all received
    402      * configuration events.
    403      */
    404     static class CollectingConfigurationListener implements
    405             ConfigurationListener
    406     {
    407         List events = new ArrayList();
    408
    409         public void configurationChanged(ConfigurationEvent event)
    410         {
    411             events.add(event);
    412         }
    413     }
    414 }