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.beanutils;
018    
019    import static org.junit.Assert.assertEquals;
020    import static org.junit.Assert.assertNotNull;
021    import static org.junit.Assert.assertNull;
022    import static org.junit.Assert.assertTrue;
023    
024    import java.util.Map;
025    
026    import org.apache.commons.configuration.HierarchicalConfiguration;
027    import org.apache.commons.configuration.SubnodeConfiguration;
028    import org.apache.commons.configuration.tree.ConfigurationNode;
029    import org.junit.Test;
030    
031    /**
032     * Test class for XMLBeanDeclaration.
033     *
034     * @since 1.3
035     * @author <a
036     * href="http://commons.apache.org/configuration/team-list.html">Commons
037     * Configuration team</a>
038     * @version $Id: TestXMLBeanDeclaration.java 1225643 2011-12-29 20:37:36Z oheger $
039     */
040    public class TestXMLBeanDeclaration
041    {
042        /** An array with some test properties. */
043        static final String[] TEST_PROPS =
044        { "firstName", "lastName", "department", "age", "hobby"};
045    
046        /** An array with the values for the test properties. */
047        static final String[] TEST_VALUES =
048        { "John", "Smith", "Engineering", "42", "TV"};
049    
050        /** An array with the names of nested (complex) properties. */
051        static final String[] COMPLEX_PROPS =
052        { "address", "car"};
053    
054        /** An array with the names of the classes of the complex properties. */
055        static final String[] COMPLEX_CLASSES =
056        { "org.apache.commons.configuration.test.AddressTest",
057                "org.apache.commons.configuration.test.CarTest"};
058    
059        /** An array with the property names of the complex properties. */
060        static final String[][] COMPLEX_ATTRIBUTES =
061        {
062        { "street", "zip", "city", "country"},
063        { "brand", "color"}};
064    
065        /** An array with the values of the complex properties. */
066        static final String[][] COMPLEX_VALUES =
067        {
068        { "Baker Street", "12354", "London", "UK"},
069        { "Bentley", "silver"}};
070    
071        /** Constant for the key with the bean declaration. */
072        static final String KEY = "myBean";
073    
074        /** Constant for the section with the variables.*/
075        static final String VARS = "variables.";
076    
077        /** Stores the object to be tested. */
078        XMLBeanDeclaration decl;
079    
080        /**
081         * Tests creating a declaration from a null node. This should cause an
082         * exception.
083         */
084        @Test(expected = IllegalArgumentException.class)
085        public void testInitFromNullNode()
086        {
087            decl = new XMLBeanDeclaration(new HierarchicalConfiguration().configurationAt(null),
088                    (ConfigurationNode) null);
089        }
090    
091        /**
092         * Tests creating a declaration from a null configuration. This should cause
093         * an exception.
094         */
095        @Test(expected = IllegalArgumentException.class)
096        public void testInitFromNullConfiguration()
097        {
098            decl = new XMLBeanDeclaration((HierarchicalConfiguration) null);
099        }
100    
101        /**
102         * Tests creating a declaration from a null configuration with a key. This
103         * should cause an exception.
104         */
105        @Test(expected = IllegalArgumentException.class)
106        public void testInitFromNullConfigurationAndKey()
107        {
108            decl = new XMLBeanDeclaration(null, KEY);
109        }
110    
111        /**
112         * Tests creating a declaration from a null configuration with a node. This
113         * should cause an exception.
114         */
115        @Test(expected = IllegalArgumentException.class)
116        public void testInitFromNullConfigurationAndNode()
117        {
118            decl = new XMLBeanDeclaration(null, new HierarchicalConfiguration()
119                    .getRoot());
120        }
121    
122        /**
123         * Tests fetching the bean's class name.
124         */
125        @Test
126        public void testGetBeanClassName()
127        {
128            HierarchicalConfiguration config = new HierarchicalConfiguration();
129            config.addProperty(KEY + "[@config-class]", getClass().getName());
130            decl = new XMLBeanDeclaration(config, KEY);
131            assertEquals("Wrong class name", getClass().getName(), decl
132                    .getBeanClassName());
133        }
134    
135        /**
136         * Tests fetching the bean's class name if it is undefined.
137         */
138        @Test
139        public void testGetBeanClassNameUndefined()
140        {
141            decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
142            assertNull(decl.getBeanClassName());
143        }
144    
145        /**
146         * Tests fetching the name of the bean factory.
147         */
148        @Test
149        public void testGetBeanFactoryName()
150        {
151            HierarchicalConfiguration config = new HierarchicalConfiguration();
152            config.addProperty(KEY + "[@config-factory]", "myFactory");
153            decl = new XMLBeanDeclaration(config, KEY);
154            assertEquals("Wrong factory name", "myFactory", decl
155                    .getBeanFactoryName());
156        }
157    
158        /**
159         * Tests fetching the name of the bean factory if it is undefined.
160         */
161        @Test
162        public void testGetBeanFactoryNameUndefined()
163        {
164            decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
165            assertNull(decl.getBeanFactoryName());
166        }
167    
168        /**
169         * Tests fetching the parameter for the bean factory.
170         */
171        @Test
172        public void testGetBeanFactoryParameter()
173        {
174            HierarchicalConfiguration config = new HierarchicalConfiguration();
175            config
176                    .addProperty(KEY + "[@config-factoryParam]",
177                            "myFactoryParameter");
178            decl = new XMLBeanDeclaration(config, KEY);
179            assertEquals("Wrong factory parameter", "myFactoryParameter", decl
180                    .getBeanFactoryParameter());
181        }
182    
183        /**
184         * Tests fetching the parameter for the bean factory if it is undefined.
185         */
186        @Test
187        public void testGetBeanFactoryParameterUndefined()
188        {
189            decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
190            assertNull(decl.getBeanFactoryParameter());
191        }
192    
193        /**
194         * Tests if the bean's properties are correctly extracted from the
195         * configuration object.
196         */
197        @Test
198        public void testGetBeanProperties()
199        {
200            HierarchicalConfiguration config = new HierarchicalConfiguration();
201            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
202            decl = new XMLBeanDeclaration(config, KEY);
203            checkProperties(decl, TEST_PROPS, TEST_VALUES);
204        }
205    
206        /**
207         * Tests obtaining the bean's properties when reserved attributes are
208         * involved. These should be ignored.
209         */
210        @Test
211        public void testGetBeanPropertiesWithReservedAttributes()
212        {
213            HierarchicalConfiguration config = new HierarchicalConfiguration();
214            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
215            config.addProperty(KEY + "[@config-testattr]", "yes");
216            config.addProperty(KEY + "[@config-anothertest]", "this, too");
217            decl = new XMLBeanDeclaration(config, KEY);
218            checkProperties(decl, TEST_PROPS, TEST_VALUES);
219        }
220    
221        /**
222         * Tests fetching properties if none are defined.
223         */
224        @Test
225        public void testGetBeanPropertiesEmpty()
226        {
227            decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
228            Map<String, Object> props = decl.getBeanProperties();
229            assertTrue("Properties found", props == null || props.isEmpty());
230        }
231    
232        /**
233         * Creates a configuration with data for testing nested bean declarations.
234         * @return the initialized test configuration
235         */
236        private HierarchicalConfiguration prepareNestedBeanDeclarations()
237        {
238            HierarchicalConfiguration config = new HierarchicalConfiguration();
239            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
240            for (int i = 0; i < COMPLEX_PROPS.length; i++)
241            {
242                setupBeanDeclaration(config, KEY + '.' + COMPLEX_PROPS[i],
243                        COMPLEX_ATTRIBUTES[i], COMPLEX_VALUES[i]);
244                config.addProperty(
245                        KEY + '.' + COMPLEX_PROPS[i] + "[@config-class]",
246                        COMPLEX_CLASSES[i]);
247            }
248            return config;
249        }
250    
251        /**
252         * Tests fetching nested bean declarations.
253         */
254        @Test
255        public void testGetNestedBeanDeclarations()
256        {
257            HierarchicalConfiguration config = prepareNestedBeanDeclarations();
258            decl = new XMLBeanDeclaration(config, KEY);
259            checkProperties(decl, TEST_PROPS, TEST_VALUES);
260    
261            Map<String, Object> nested = decl.getNestedBeanDeclarations();
262            assertEquals("Wrong number of nested declarations",
263                    COMPLEX_PROPS.length, nested.size());
264            for (int i = 0; i < COMPLEX_PROPS.length; i++)
265            {
266                XMLBeanDeclaration d = (XMLBeanDeclaration) nested
267                        .get(COMPLEX_PROPS[i]);
268                assertNotNull("No declaration found for " + COMPLEX_PROPS[i], d);
269                checkProperties(d, COMPLEX_ATTRIBUTES[i], COMPLEX_VALUES[i]);
270                assertEquals("Wrong bean class", COMPLEX_CLASSES[i], d
271                        .getBeanClassName());
272            }
273        }
274    
275        /**
276         * Tests whether the factory method for creating nested bean declarations
277         * gets called.
278         */
279        @Test
280        public void testGetNestedBeanDeclarationsFactoryMethod()
281        {
282            HierarchicalConfiguration config = prepareNestedBeanDeclarations();
283            decl = new XMLBeanDeclaration(config, KEY)
284            {
285                @Override
286                protected BeanDeclaration createBeanDeclaration(
287                        ConfigurationNode node)
288                {
289                    return new XMLBeanDeclarationTestImpl(getConfiguration()
290                            .configurationAt(node.getName()), node);
291                }
292            };
293            Map<String, Object> nested = decl.getNestedBeanDeclarations();
294            for (int i = 0; i < COMPLEX_PROPS.length; i++)
295            {
296                Object d = nested.get(COMPLEX_PROPS[i]);
297                assertTrue("Wrong class for bean declaration: " + d,
298                        d instanceof XMLBeanDeclarationTestImpl);
299            }
300        }
301    
302        /**
303         * Tests fetching nested bean declarations if none are defined.
304         */
305        @Test
306        public void testGetNestedBeanDeclarationsEmpty()
307        {
308            HierarchicalConfiguration config = new HierarchicalConfiguration();
309            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
310            decl = new XMLBeanDeclaration(config, KEY);
311            Map<String, Object> nested = decl.getNestedBeanDeclarations();
312            assertTrue("Found nested declarations", nested == null
313                    || nested.isEmpty());
314        }
315    
316        /**
317         * Tests whether interpolation of bean properties works.
318         */
319        @Test
320        public void testGetInterpolatedBeanProperties()
321        {
322            HierarchicalConfiguration config = new HierarchicalConfiguration();
323            String[] varValues = new String[TEST_PROPS.length];
324            for(int i = 0; i < TEST_PROPS.length; i++)
325            {
326                varValues[i] = "${" + VARS + TEST_PROPS[i] + "}";
327                config.addProperty(VARS + TEST_PROPS[i], TEST_VALUES[i]);
328            }
329            setupBeanDeclaration(config, KEY, TEST_PROPS, varValues);
330            decl = new XMLBeanDeclaration(config, KEY);
331            checkProperties(decl, TEST_PROPS, TEST_VALUES);
332        }
333    
334        /**
335         * Tests constructing a bean declaration from an undefined key. This should
336         * cause an exception.
337         */
338        @Test(expected = IllegalArgumentException.class)
339        public void testInitFromUndefinedKey()
340        {
341            HierarchicalConfiguration config = new HierarchicalConfiguration();
342            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
343            decl = new XMLBeanDeclaration(config, "undefined_key");
344        }
345    
346        /**
347         * Tests constructing a bean declaration from a key, which is undefined when
348         * the optional flag is set. In this case an empty declaration should be
349         * created, which can be used for creating beans as long as a default class
350         * is provided.
351         */
352        @Test
353        public void testInitFromUndefinedKeyOptional()
354        {
355            HierarchicalConfiguration config = new HierarchicalConfiguration();
356            setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
357            decl = new XMLBeanDeclaration(config, "undefined_key", true);
358            assertNull("Found a bean class", decl.getBeanClassName());
359        }
360    
361        /**
362         * Tests constructing a bean declaration from a key with multiple values.
363         * This should cause an exception because keys must be unique.
364         */
365        @Test(expected = IllegalArgumentException.class)
366        public void testInitFromMultiValueKey()
367        {
368            HierarchicalConfiguration config = new HierarchicalConfiguration();
369            config.addProperty(KEY, "myFirstKey");
370            config.addProperty(KEY, "mySecondKey");
371            decl = new XMLBeanDeclaration(config, KEY);
372        }
373    
374        /**
375         * Initializes a configuration object with a bean declaration. Under the
376         * specified key the given properties will be added.
377         *
378         * @param config the configuration to initialize
379         * @param key the key of the bean declaration
380         * @param names an array with the names of the properties
381         * @param values an array with the corresponding values
382         */
383        private void setupBeanDeclaration(HierarchicalConfiguration config,
384                String key, String[] names, String[] values)
385        {
386            for (int i = 0; i < names.length; i++)
387            {
388                config.addProperty(key + "[@" + names[i] + "]", values[i]);
389            }
390        }
391    
392        /**
393         * Checks the properties returned by a bean declaration.
394         *
395         * @param beanDecl the bean declaration
396         * @param names an array with the expected property names
397         * @param values an array with the expected property values
398         */
399        private void checkProperties(BeanDeclaration beanDecl, String[] names,
400                String[] values)
401        {
402            Map<String, Object> props = beanDecl.getBeanProperties();
403            assertEquals("Wrong number of properties", names.length, props.size());
404            for (int i = 0; i < names.length; i++)
405            {
406                assertTrue("Property " + names[i] + " not contained", props
407                        .containsKey(names[i]));
408                assertEquals("Wrong value for property " + names[i], values[i],
409                        props.get(names[i]));
410            }
411        }
412    
413        /**
414         * A helper class used for testing the createBeanDeclaration() factory
415         * method.
416         */
417        private static class XMLBeanDeclarationTestImpl extends XMLBeanDeclaration
418        {
419            public XMLBeanDeclarationTestImpl(SubnodeConfiguration config,
420                    ConfigurationNode node)
421            {
422                super(config, node);
423            }
424        }
425    }