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.beanutils;
18  
19  import java.util.Map;
20  
21  import org.apache.commons.configuration.HierarchicalConfiguration;
22  import org.apache.commons.configuration.SubnodeConfiguration;
23  import org.apache.commons.configuration.tree.ConfigurationNode;
24  
25  import junit.framework.TestCase;
26  
27  /***
28   * Test class for XMLBeanDeclaration.
29   *
30   * @since 1.3
31   * @author Oliver Heger
32   * @version $Id: TestXMLBeanDeclaration.java 670739 2008-06-23 20:36:37Z oheger $
33   */
34  public class TestXMLBeanDeclaration extends TestCase
35  {
36      /*** An array with some test properties. */
37      static final String[] TEST_PROPS =
38      { "firstName", "lastName", "department", "age", "hobby"};
39  
40      /*** An array with the values for the test properties. */
41      static final String[] TEST_VALUES =
42      { "John", "Smith", "Engineering", "42", "TV"};
43  
44      /*** An array with the names of nested (complex) properties. */
45      static final String[] COMPLEX_PROPS =
46      { "address", "car"};
47  
48      /*** An array with the names of the classes of the complex properties. */
49      static final String[] COMPLEX_CLASSES =
50      { "org.apache.commons.configuration.test.AddressTest",
51              "org.apache.commons.configuration.test.CarTest"};
52  
53      /*** An array with the property names of the complex properties. */
54      static final String[][] COMPLEX_ATTRIBUTES =
55      {
56      { "street", "zip", "city", "country"},
57      { "brand", "color"}};
58  
59      /*** An array with the values of the complex properties. */
60      static final String[][] COMPLEX_VALUES =
61      {
62      { "Baker Street", "12354", "London", "UK"},
63      { "Bentley", "silver"}};
64  
65      /*** Constant for the key with the bean declaration. */
66      static final String KEY = "myBean";
67  
68      /*** Constant for the section with the variables.*/
69      static final String VARS = "variables.";
70  
71      /*** Stores the object to be tested. */
72      XMLBeanDeclaration decl;
73  
74      /***
75       * Tests creating a declaration from a null node. This should cause an
76       * exception.
77       */
78      public void testInitFromNullNode()
79      {
80          try
81          {
82              decl = new XMLBeanDeclaration(new HierarchicalConfiguration().configurationAt(null),
83                      (ConfigurationNode) null);
84              fail("Could init declaration with null node!");
85          }
86          catch (IllegalArgumentException iex)
87          {
88              // ok
89          }
90      }
91  
92      /***
93       * Tests creating a declaration from a null configuration. This should cause
94       * an exception.
95       */
96      public void testInitFromNullConfiguration()
97      {
98          try
99          {
100             decl = new XMLBeanDeclaration((HierarchicalConfiguration) null);
101             fail("Could init declaration with null configuration!");
102         }
103         catch (IllegalArgumentException iex)
104         {
105             // ok
106         }
107     }
108 
109     /***
110      * Tests creating a declaration from a null configuration with a key. This
111      * should cause an exception.
112      */
113     public void testInitFromNullConfigurationAndKey()
114     {
115         try
116         {
117             decl = new XMLBeanDeclaration(null, KEY);
118             fail("Could init declaration with null configuration and key!");
119         }
120         catch (IllegalArgumentException iex)
121         {
122             // ok
123         }
124     }
125 
126     /***
127      * Tests creating a declaration from a null configuration with a node. This
128      * should cause an exception.
129      */
130     public void testInitFromNullConfigurationAndNode()
131     {
132         try
133         {
134             decl = new XMLBeanDeclaration(null, new HierarchicalConfiguration()
135                     .getRoot());
136             fail("Could init declaration with null configuration and node!");
137         }
138         catch (IllegalArgumentException iex)
139         {
140             // ok
141         }
142     }
143 
144     /***
145      * Tests fetching the bean's class name.
146      */
147     public void testGetBeanClassName()
148     {
149         HierarchicalConfiguration config = new HierarchicalConfiguration();
150         config.addProperty(KEY + "[@config-class]", getClass().getName());
151         decl = new XMLBeanDeclaration(config, KEY);
152         assertEquals("Wrong class name", getClass().getName(), decl
153                 .getBeanClassName());
154     }
155 
156     /***
157      * Tests fetching the bean's class name if it is undefined.
158      */
159     public void testGetBeanClassNameUndefined()
160     {
161         decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
162         assertNull(decl.getBeanClassName());
163     }
164 
165     /***
166      * Tests fetching the name of the bean factory.
167      */
168     public void testGetBeanFactoryName()
169     {
170         HierarchicalConfiguration config = new HierarchicalConfiguration();
171         config.addProperty(KEY + "[@config-factory]", "myFactory");
172         decl = new XMLBeanDeclaration(config, KEY);
173         assertEquals("Wrong factory name", "myFactory", decl
174                 .getBeanFactoryName());
175     }
176 
177     /***
178      * Tests fetching the name of the bean factory if it is undefined.
179      */
180     public void testGetBeanFactoryNameUndefined()
181     {
182         decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
183         assertNull(decl.getBeanFactoryName());
184     }
185 
186     /***
187      * Tests fetching the paramter for the bean factory.
188      */
189     public void testGetBeanFactoryParameter()
190     {
191         HierarchicalConfiguration config = new HierarchicalConfiguration();
192         config
193                 .addProperty(KEY + "[@config-factoryParam]",
194                         "myFactoryParameter");
195         decl = new XMLBeanDeclaration(config, KEY);
196         assertEquals("Wrong factory parameter", "myFactoryParameter", decl
197                 .getBeanFactoryParameter());
198     }
199 
200     /***
201      * Tests fetching the paramter for the bean factory if it is undefined.
202      */
203     public void testGetBeanFactoryParameterUndefined()
204     {
205         decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
206         assertNull(decl.getBeanFactoryParameter());
207     }
208 
209     /***
210      * Tests if the bean's properties are correctly extracted from the
211      * configuration object.
212      */
213     public void testGetBeanProperties()
214     {
215         HierarchicalConfiguration config = new HierarchicalConfiguration();
216         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
217         decl = new XMLBeanDeclaration(config, KEY);
218         checkProperties(decl, TEST_PROPS, TEST_VALUES);
219     }
220 
221     /***
222      * Tests obtaining the bean's properties when reserved attributes are
223      * involved. These should be ignored.
224      */
225     public void testGetBeanPropertiesWithReservedAttributes()
226     {
227         HierarchicalConfiguration config = new HierarchicalConfiguration();
228         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
229         config.addProperty(KEY + "[@config-testattr]", "yes");
230         config.addProperty(KEY + "[@config-anothertest]", "this, too");
231         decl = new XMLBeanDeclaration(config, KEY);
232         checkProperties(decl, TEST_PROPS, TEST_VALUES);
233     }
234 
235     /***
236      * Tests fetching properties if none are defined.
237      */
238     public void testGetBeanPropertiesEmpty()
239     {
240         decl = new XMLBeanDeclaration(new HierarchicalConfiguration());
241         Map props = decl.getBeanProperties();
242         assertTrue("Properties found", props == null || props.isEmpty());
243     }
244 
245     /***
246      * Creates a configuration with data for testing nested bean declarations.
247      * @return the initialized test configuration
248      */
249     private HierarchicalConfiguration prepareNestedBeanDeclarations()
250     {
251         HierarchicalConfiguration config = new HierarchicalConfiguration();
252         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
253         for (int i = 0; i < COMPLEX_PROPS.length; i++)
254         {
255             setupBeanDeclaration(config, KEY + '.' + COMPLEX_PROPS[i],
256                     COMPLEX_ATTRIBUTES[i], COMPLEX_VALUES[i]);
257             config.addProperty(
258                     KEY + '.' + COMPLEX_PROPS[i] + "[@config-class]",
259                     COMPLEX_CLASSES[i]);
260         }
261         return config;
262     }
263 
264     /***
265      * Tests fetching nested bean declarations.
266      */
267     public void testGetNestedBeanDeclarations()
268     {
269         HierarchicalConfiguration config = prepareNestedBeanDeclarations();
270         decl = new XMLBeanDeclaration(config, KEY);
271         checkProperties(decl, TEST_PROPS, TEST_VALUES);
272 
273         Map nested = decl.getNestedBeanDeclarations();
274         assertEquals("Wrong number of nested declarations",
275                 COMPLEX_PROPS.length, nested.size());
276         for (int i = 0; i < COMPLEX_PROPS.length; i++)
277         {
278             XMLBeanDeclaration d = (XMLBeanDeclaration) nested
279                     .get(COMPLEX_PROPS[i]);
280             assertNotNull("No declaration found for " + COMPLEX_PROPS[i], d);
281             checkProperties(d, COMPLEX_ATTRIBUTES[i], COMPLEX_VALUES[i]);
282             assertEquals("Wrong bean class", COMPLEX_CLASSES[i], d
283                     .getBeanClassName());
284         }
285     }
286 
287     /***
288      * Tests whether the factory method for creating nested bean declarations
289      * gets called.
290      */
291     public void testGetNestedBeanDeclarationsFactoryMethod()
292     {
293         HierarchicalConfiguration config = prepareNestedBeanDeclarations();
294         decl = new XMLBeanDeclaration(config, KEY)
295         {
296             protected BeanDeclaration createBeanDeclaration(
297                     ConfigurationNode node)
298             {
299                 return new XMLBeanDeclarationTestImpl(getConfiguration()
300                         .configurationAt(node.getName()), node);
301             }
302         };
303         Map nested = decl.getNestedBeanDeclarations();
304         for (int i = 0; i < COMPLEX_PROPS.length; i++)
305         {
306             Object d = nested.get(COMPLEX_PROPS[i]);
307             assertTrue("Wrong class for bean declaration: " + d,
308                     d instanceof XMLBeanDeclarationTestImpl);
309         }
310     }
311 
312     /***
313      * Tests fetching nested bean declarations if none are defined.
314      */
315     public void testGetNestedBeanDeclarationsEmpty()
316     {
317         HierarchicalConfiguration config = new HierarchicalConfiguration();
318         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
319         decl = new XMLBeanDeclaration(config, KEY);
320         Map nested = decl.getNestedBeanDeclarations();
321         assertTrue("Found nested declarations", nested == null
322                 || nested.isEmpty());
323     }
324 
325     /***
326      * Tests whether interpolation of bean properties works.
327      */
328     public void testGetInterpolatedBeanProperties()
329     {
330         HierarchicalConfiguration config = new HierarchicalConfiguration();
331         String[] varValues = new String[TEST_PROPS.length];
332         for(int i = 0; i < TEST_PROPS.length; i++)
333         {
334             varValues[i] = "${" + VARS + TEST_PROPS[i] + "}";
335             config.addProperty(VARS + TEST_PROPS[i], TEST_VALUES[i]);
336         }
337         setupBeanDeclaration(config, KEY, TEST_PROPS, varValues);
338         decl = new XMLBeanDeclaration(config, KEY);
339         checkProperties(decl, TEST_PROPS, TEST_VALUES);
340     }
341 
342     /***
343      * Tests constructing a bean declaration from an undefined key. This should
344      * cause an exception.
345      */
346     public void testInitFromUndefinedKey()
347     {
348         HierarchicalConfiguration config = new HierarchicalConfiguration();
349         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
350         try
351         {
352             decl = new XMLBeanDeclaration(config, "undefined_key");
353             fail("Could create declaration from an undefined key!");
354         }
355         catch (IllegalArgumentException iex)
356         {
357             // ok
358         }
359     }
360 
361     /***
362      * Tests constructing a bean declaration from a key, which is undefined when
363      * the optional flag is set. In this case an empty declaration should be
364      * created, which can be used for creating beans as long as a default class
365      * is provided.
366      */
367     public void testInitFromUndefinedKeyOptional()
368     {
369         HierarchicalConfiguration config = new HierarchicalConfiguration();
370         setupBeanDeclaration(config, KEY, TEST_PROPS, TEST_VALUES);
371         decl = new XMLBeanDeclaration(config, "undefined_key", true);
372         assertNull("Found a bean class", decl.getBeanClassName());
373     }
374 
375     /***
376      * Tests constructing a bean declaration from a key with multiple values.
377      * This should cause an exception because keys must be unique.
378      */
379     public void testInitFromMultiValueKey()
380     {
381         HierarchicalConfiguration config = new HierarchicalConfiguration();
382         config.addProperty(KEY, "myFirstKey");
383         config.addProperty(KEY, "mySecondKey");
384         try
385         {
386             decl = new XMLBeanDeclaration(config, KEY);
387             fail("Could create declaration from multi-valued property!");
388         }
389         catch (IllegalArgumentException iex)
390         {
391             // ok
392         }
393     }
394 
395     /***
396      * Initializes a configuration object with a bean declaration. Under the
397      * specified key the given properties will be added.
398      *
399      * @param config the configuration to initialize
400      * @param key the key of the bean declaration
401      * @param names an array with the names of the properties
402      * @param values an array with the corresponding values
403      */
404     private void setupBeanDeclaration(HierarchicalConfiguration config,
405             String key, String[] names, String[] values)
406     {
407         for (int i = 0; i < names.length; i++)
408         {
409             config.addProperty(key + "[@" + names[i] + "]", values[i]);
410         }
411     }
412 
413     /***
414      * Checks the properties returned by a bean declaration.
415      *
416      * @param beanDecl the bean declaration
417      * @param names an array with the expected property names
418      * @param values an array with the expected property values
419      */
420     private void checkProperties(BeanDeclaration beanDecl, String[] names,
421             String[] values)
422     {
423         Map props = beanDecl.getBeanProperties();
424         assertEquals("Wrong number of properties", names.length, props.size());
425         for (int i = 0; i < names.length; i++)
426         {
427             assertTrue("Property " + names[i] + " not contained", props
428                     .containsKey(names[i]));
429             assertEquals("Wrong value for property " + names[i], values[i],
430                     props.get(names[i]));
431         }
432     }
433 
434     /***
435      * A helper class used for testing the createBeanDeclaration() factory
436      * method.
437      */
438     private static class XMLBeanDeclarationTestImpl extends XMLBeanDeclaration
439     {
440         public XMLBeanDeclarationTestImpl(SubnodeConfiguration config,
441                 ConfigurationNode node)
442         {
443             super(config, node);
444         }
445     }
446 }