001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.configuration;
018    
019    import static org.junit.Assert.assertEquals;
020    import static org.junit.Assert.assertFalse;
021    import static org.junit.Assert.assertTrue;
022    
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Collection;
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    
031    import org.apache.commons.collections.CollectionUtils;
032    import org.apache.commons.configuration.event.ConfigurationEvent;
033    import org.apache.commons.configuration.event.ConfigurationListener;
034    import org.junit.Test;
035    
036    /**
037     * A test class for some of the basic functionality implemented by
038     * AbstractConfiguration.
039     *
040     * @version $Id: TestAbstractConfigurationBasicFeatures.java 1222823 2011-12-23 20:03:10Z oheger $
041     */
042    public class TestAbstractConfigurationBasicFeatures
043    {
044        /** Constant for the prefix of test keys.*/
045        private static final String KEY_PREFIX = "key";
046    
047        /** Constant for the number of properties in tests for copy operations.*/
048        private static final int PROP_COUNT = 12;
049    
050        /**
051         * Tests the clear() implementation of AbstractConfiguration if the iterator
052         * returned by getKeys() does not support the remove() operation.
053         */
054        @Test
055        public void testClearIteratorNoRemove()
056        {
057            AbstractConfiguration config = new TestConfigurationImpl(
058                    new BaseConfiguration())
059            {
060                // return an iterator that does not support remove operations
061                @Override
062                public Iterator<String> getKeys()
063                {
064                    Collection<String> keyCol = new ArrayList<String>();
065                    CollectionUtils.addAll(keyCol, getUnderlyingConfiguration()
066                            .getKeys());
067                    String[] keys = keyCol.toArray(new String[keyCol.size()]);
068                    return Arrays.asList(keys).iterator();
069                }
070            };
071            for (int i = 0; i < 20; i++)
072            {
073                config.addProperty("key" + i, "value" + i);
074            }
075            config.clear();
076            assertTrue("Configuration not empty", config.isEmpty());
077        }
078    
079        /**
080         * Tests escaping the variable marker, so that no interpolation will be
081         * performed.
082         */
083        @Test
084        public void testInterpolateEscape()
085        {
086            AbstractConfiguration config = new TestConfigurationImpl(
087                    new PropertiesConfiguration());
088            config
089                    .addProperty(
090                            "mypath",
091                            "$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar\\,$${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar");
092            assertEquals(
093                    "Wrong interpolated value",
094                    "${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc.jar,${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cu.jar",
095                    config.getString("mypath"));
096        }
097    
098        /**
099         * Tests adding list properties. The single elements of the list should be
100         * added.
101         */
102        @Test
103        public void testAddPropertyList()
104        {
105            checkAddListProperty(new TestConfigurationImpl(
106                    new PropertiesConfiguration()));
107        }
108    
109        /**
110         * Tests adding list properties when delimiter parsing is disabled.
111         */
112        @Test
113        public void testAddPropertyListNoDelimiterParsing()
114        {
115            AbstractConfiguration config = new TestConfigurationImpl(
116                    new PropertiesConfiguration());
117            config.setDelimiterParsingDisabled(true);
118            checkAddListProperty(config);
119        }
120    
121        /**
122         * Helper method for adding properties with multiple values.
123         *
124         * @param config the configuration to be used for testing
125         */
126        private void checkAddListProperty(AbstractConfiguration config)
127        {
128            config.addProperty("test", "value1");
129            Object[] lstValues1 = new Object[]
130            { "value2", "value3" };
131            Object[] lstValues2 = new Object[]
132            { "value4", "value5", "value6" };
133            config.addProperty("test", lstValues1);
134            config.addProperty("test", Arrays.asList(lstValues2));
135            List<Object> lst = config.getList("test");
136            assertEquals("Wrong number of list elements", 6, lst.size());
137            for (int i = 0; i < lst.size(); i++)
138            {
139                assertEquals("Wrong list element at " + i, "value" + (i + 1), lst
140                        .get(i));
141            }
142        }
143    
144        /**
145         * Tests the copy() method.
146         */
147        @Test
148        public void testCopy()
149        {
150            AbstractConfiguration config = setUpDestConfig();
151            Configuration srcConfig = setUpSourceConfig();
152            config.copy(srcConfig);
153            for (int i = 0; i < PROP_COUNT; i++)
154            {
155                String key = KEY_PREFIX + i;
156                if (srcConfig.containsKey(key))
157                {
158                    assertEquals("Value not replaced: " + key, srcConfig
159                            .getProperty(key), config.getProperty(key));
160                }
161                else
162                {
163                    assertEquals("Value modified: " + key, "value" + i, config
164                            .getProperty(key));
165                }
166            }
167        }
168    
169        /**
170         * Tests the copy() method when properties with multiple values and escaped
171         * list delimiters are involved.
172         */
173        @Test
174        public void testCopyWithLists()
175        {
176            Configuration srcConfig = setUpSourceConfig();
177            AbstractConfiguration config = setUpDestConfig();
178            config.copy(srcConfig);
179            checkListProperties(config);
180        }
181    
182        /**
183         * Tests the events generated by a copy() operation.
184         */
185        @Test
186        public void testCopyEvents()
187        {
188            AbstractConfiguration config = setUpDestConfig();
189            Configuration srcConfig = setUpSourceConfig();
190            CollectingConfigurationListener l = new CollectingConfigurationListener();
191            config.addConfigurationListener(l);
192            config.copy(srcConfig);
193            checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_SET_PROPERTY);
194        }
195    
196        /**
197         * Tests copying a null configuration. This should be a noop.
198         */
199        @Test
200        public void testCopyNull()
201        {
202            AbstractConfiguration config = setUpDestConfig();
203            config.copy(null);
204            ConfigurationAssert.assertEquals(setUpDestConfig(), config);
205        }
206    
207        /**
208         * Tests the append() method.
209         */
210        @Test
211        public void testAppend()
212        {
213            AbstractConfiguration config = setUpDestConfig();
214            Configuration srcConfig = setUpSourceConfig();
215            config.append(srcConfig);
216            for (int i = 0; i < PROP_COUNT; i++)
217            {
218                String key = KEY_PREFIX + i;
219                if (srcConfig.containsKey(key))
220                {
221                    List<Object> values = config.getList(key);
222                    assertEquals("Value not added: " + key, 2, values.size());
223                    assertEquals("Wrong value 1 for " + key, "value" + i, values
224                            .get(0));
225                    assertEquals("Wrong value 2 for " + key, "src" + i, values
226                            .get(1));
227                }
228                else
229                {
230                    assertEquals("Value modified: " + key, "value" + i, config
231                            .getProperty(key));
232                }
233            }
234        }
235    
236        /**
237         * Tests the append() method when properties with multiple values and
238         * escaped list delimiters are involved.
239         */
240        @Test
241        public void testAppendWithLists()
242        {
243            AbstractConfiguration config = setUpDestConfig();
244            config.append(setUpSourceConfig());
245            checkListProperties(config);
246        }
247    
248        /**
249         * Tests the events generated by an append() operation.
250         */
251        @Test
252        public void testAppendEvents()
253        {
254            AbstractConfiguration config = setUpDestConfig();
255            Configuration srcConfig = setUpSourceConfig();
256            CollectingConfigurationListener l = new CollectingConfigurationListener();
257            config.addConfigurationListener(l);
258            config.append(srcConfig);
259            checkCopyEvents(l, srcConfig, AbstractConfiguration.EVENT_ADD_PROPERTY);
260        }
261    
262        /**
263         * Tests appending a null configuration. This should be a noop.
264         */
265        @Test
266        public void testAppendNull()
267        {
268            AbstractConfiguration config = setUpDestConfig();
269            config.append(null);
270            ConfigurationAssert.assertEquals(setUpDestConfig(), config);
271        }
272    
273        /**
274         * Tests whether environment variables can be interpolated.
275         */
276        @Test
277        public void testInterpolateEnvironmentVariables()
278        {
279            AbstractConfiguration config = new TestConfigurationImpl(
280                    new PropertiesConfiguration());
281            EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
282            Map<String, Object> env = new HashMap<String, Object>();
283            for (Iterator<String> it = envConfig.getKeys(); it.hasNext();)
284            {
285                String key = it.next();
286                String propKey = "envtest." + key;
287                env.put(propKey, envConfig.getString(key));
288                config.addProperty(propKey, "${env:" + key + "}");
289            }
290            assertFalse("No environment properties", env.isEmpty());
291            for (Map.Entry<String, Object> e : env.entrySet())
292            {
293                assertEquals("Wrong value for " + e.getKey(), e.getValue(), config
294                        .getString(e.getKey()));
295            }
296        }
297    
298        /**
299         * Tests getList() for single non-string values.
300         */
301        @Test
302        public void testGetListNonString()
303        {
304            checkGetListScalar(Integer.valueOf(42));
305            checkGetListScalar(Long.valueOf(42));
306            checkGetListScalar(Short.valueOf((short) 42));
307            checkGetListScalar(Byte.valueOf((byte) 42));
308            checkGetListScalar(Float.valueOf(42));
309            checkGetListScalar(Double.valueOf(42));
310            checkGetListScalar(Boolean.TRUE);
311    }
312    
313        /**
314         * Tests getStringArray() for single son-string values.
315         */
316        @Test
317        public void testGetStringArrayNonString()
318        {
319            checkGetStringArrayScalar(Integer.valueOf(42));
320            checkGetStringArrayScalar(Long.valueOf(42));
321            checkGetStringArrayScalar(Short.valueOf((short) 42));
322            checkGetStringArrayScalar(Byte.valueOf((byte) 42));
323            checkGetStringArrayScalar(Float.valueOf(42));
324            checkGetStringArrayScalar(Double.valueOf(42));
325            checkGetStringArrayScalar(Boolean.TRUE);
326        }
327    
328        /**
329         * Helper method for checking getList() if the property value is a scalar.
330         * @param value the value of the property
331         */
332        private void checkGetListScalar(Object value)
333        {
334            BaseConfiguration config = new BaseConfiguration();
335            config.addProperty(KEY_PREFIX, value);
336            List<Object> lst = config.getList(KEY_PREFIX);
337            assertEquals("Wrong number of values", 1, lst.size());
338            assertEquals("Wrong value", value.toString(), lst.get(0));
339        }
340    
341        /**
342         * Helper method for checking getStringArray() if the property value is a
343         * scalar.
344         *
345         * @param value the value of the property
346         */
347        private void checkGetStringArrayScalar(Object value)
348        {
349            BaseConfiguration config = new BaseConfiguration();
350            config.addProperty(KEY_PREFIX, value);
351            String[] array = config.getStringArray(KEY_PREFIX);
352            assertEquals("Weong number of elements", 1, array.length);
353            assertEquals("Wrong value", value.toString(), array[0]);
354        }
355    
356        /**
357         * Tests whether interpolation works in variable names.
358         */
359        @Test
360        public void testNestedVariableInterpolation()
361        {
362            BaseConfiguration config = new BaseConfiguration();
363            config.getSubstitutor().setEnableSubstitutionInVariables(true);
364            config.addProperty("java.version", "1.4");
365            config.addProperty("jre-1.4", "C:\\java\\1.4");
366            config.addProperty("jre.path", "${jre-${java.version}}");
367            assertEquals("Wrong path", "C:\\java\\1.4",
368                    config.getString("jre.path"));
369        }
370    
371        /**
372         * Creates the source configuration for testing the copy() and append()
373         * methods. This configuration contains keys with an odd index and values
374         * starting with the prefix "src". There are also some list properties.
375         *
376         * @return the source configuration for copy operations
377         */
378        private Configuration setUpSourceConfig()
379        {
380            BaseConfiguration config = new BaseConfiguration();
381            for (int i = 1; i < PROP_COUNT; i += 2)
382            {
383                config.addProperty(KEY_PREFIX + i, "src" + i);
384            }
385            config.addProperty("list1", "1,2,3");
386            config.addProperty("list2", "3\\,1415,9\\,81");
387            return config;
388        }
389    
390        /**
391         * Creates the destination configuration for testing the copy() and append()
392         * methods. This configuration contains keys with a running index and
393         * corresponding values starting with the prefix "value".
394         *
395         * @return the destination configuration for copy operations
396         */
397        private AbstractConfiguration setUpDestConfig()
398        {
399            AbstractConfiguration config = new TestConfigurationImpl(
400                    new PropertiesConfiguration());
401            for (int i = 0; i < PROP_COUNT; i++)
402            {
403                config.addProperty(KEY_PREFIX + i, "value" + i);
404            }
405            return config;
406        }
407    
408        /**
409         * Tests the values of list properties after a copy operation.
410         *
411         * @param config the configuration to test
412         */
413        private void checkListProperties(Configuration config)
414        {
415            List<Object> values = config.getList("list1");
416            assertEquals("Wrong number of elements in list 1", 3, values.size());
417            values = config.getList("list2");
418            assertEquals("Wrong number of elements in list 2", 2, values.size());
419            assertEquals("Wrong value 1", "3,1415", values.get(0));
420            assertEquals("Wrong value 2", "9,81", values.get(1));
421        }
422    
423        /**
424         * Tests whether the correct events are received for a copy operation.
425         *
426         * @param l the event listener
427         * @param src the configuration that was copied
428         * @param eventType the expected event type
429         */
430        private void checkCopyEvents(CollectingConfigurationListener l,
431                Configuration src, int eventType)
432        {
433            Map<String, ConfigurationEvent> events = new HashMap<String, ConfigurationEvent>();
434            for (ConfigurationEvent e : l.events)
435            {
436                assertEquals("Wrong event type", eventType, e.getType());
437                assertTrue("Unknown property: " + e.getPropertyName(), src
438                        .containsKey(e.getPropertyName()));
439                assertEquals("Wrong property value for " + e.getPropertyName(), e
440                        .getPropertyValue(), src.getProperty(e.getPropertyName()));
441                if (!e.isBeforeUpdate())
442                {
443                    assertTrue("After event without before event", events
444                            .containsKey(e.getPropertyName()));
445                }
446                else
447                {
448                    events.put(e.getPropertyName(), e);
449                }
450            }
451    
452            for (Iterator<String> it = src.getKeys(); it.hasNext();)
453            {
454                String key = it.next();
455                assertTrue("No event received for key " + key, events
456                        .containsKey(key));
457            }
458        }
459    
460        /**
461         * A test configuration implementation. This implementation inherits
462         * directly from AbstractConfiguration. For implementing the required
463         * functionality another implementation of AbstractConfiguration is used;
464         * all methods that need to be implemented delegate to this wrapped
465         * configuration.
466         */
467        static class TestConfigurationImpl extends AbstractConfiguration
468        {
469            /** Stores the underlying configuration. */
470            private AbstractConfiguration config;
471    
472            public AbstractConfiguration getUnderlyingConfiguration()
473            {
474                return config;
475            }
476    
477            public TestConfigurationImpl(AbstractConfiguration wrappedConfig)
478            {
479                config = wrappedConfig;
480            }
481    
482            @Override
483            protected void addPropertyDirect(String key, Object value)
484            {
485                config.addPropertyDirect(key, value);
486            }
487    
488            public boolean containsKey(String key)
489            {
490                return config.containsKey(key);
491            }
492    
493            public Iterator<String> getKeys()
494            {
495                return config.getKeys();
496            }
497    
498            public Object getProperty(String key)
499            {
500                return config.getProperty(key);
501            }
502    
503            public boolean isEmpty()
504            {
505                return config.isEmpty();
506            }
507    
508            @Override
509            protected void clearPropertyDirect(String key)
510            {
511                config.clearPropertyDirect(key);
512            }
513        }
514    
515        /**
516         * An event listener implementation that simply collects all received
517         * configuration events.
518         */
519        static class CollectingConfigurationListener implements
520                ConfigurationListener
521        {
522            List<ConfigurationEvent> events = new ArrayList<ConfigurationEvent>();
523    
524            public void configurationChanged(ConfigurationEvent event)
525            {
526                events.add(event);
527            }
528        }
529    }