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 }