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.HashSet;
20  import java.util.List;
21  import java.util.NoSuchElementException;
22  import java.util.Set;
23  
24  import org.apache.commons.collections.CollectionUtils;
25  import org.apache.commons.configuration.tree.ConfigurationNode;
26  import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
27  
28  import junit.framework.TestCase;
29  
30  /***
31   * Test case for SubnodeConfiguration.
32   *
33   * @author Oliver Heger
34   * @version $Id: TestSubnodeConfiguration.java 439648 2006-09-02 20:42:10Z oheger $
35   */
36  public class TestSubnodeConfiguration extends TestCase
37  {
38      /*** An array with names of tables (test data). */
39      private static final String[] TABLE_NAMES =
40      { "documents", "users" };
41  
42      /*** An array with the fields of the test tables (test data). */
43      private static final String[][] TABLE_FIELDS =
44      {
45      { "docid", "docname", "author", "dateOfCreation", "version", "size" },
46      { "userid", "uname", "firstName", "lastName" } };
47  
48      /*** The parent configuration. */
49      HierarchicalConfiguration parent;
50  
51      /*** The subnode configuration to be tested. */
52      SubnodeConfiguration config;
53  
54      /*** Stores the root node of the subnode config. */
55      ConfigurationNode subnode;
56  
57      /*** Stores a counter for the created nodes. */
58      int nodeCounter;
59  
60      protected void setUp() throws Exception
61      {
62          super.setUp();
63          parent = setUpParentConfig();
64          nodeCounter = 0;
65      }
66  
67      /***
68       * Tests creation of a subnode config.
69       */
70      public void testInitSubNodeConfig()
71      {
72          setUpSubnodeConfig();
73          assertSame("Wrong root node in subnode", getSubnodeRoot(parent), config
74                  .getRoot());
75          assertSame("Wrong parent config", parent, config.getParent());
76      }
77  
78      /***
79       * Tests constructing a subnode configuration with a null parent. This
80       * should cause an exception.
81       */
82      public void testInitSubNodeConfigWithNullParent()
83      {
84          try
85          {
86              config = new SubnodeConfiguration(null, getSubnodeRoot(parent));
87              fail("Could set a null parent config!");
88          }
89          catch (IllegalArgumentException iex)
90          {
91              // ok
92          }
93      }
94  
95      /***
96       * Tests constructing a subnode configuration with a null root node. This
97       * should cause an exception.
98       */
99      public void testInitSubNodeConfigWithNullNode()
100     {
101         try
102         {
103             config = new SubnodeConfiguration(parent, null);
104             fail("Could set a null root node!");
105         }
106         catch (IllegalArgumentException iex)
107         {
108             // ok
109         }
110     }
111 
112     /***
113      * Tests if properties of the sub node can be accessed.
114      */
115     public void testGetProperties()
116     {
117         setUpSubnodeConfig();
118         assertEquals("Wrong table name", TABLE_NAMES[0], config
119                 .getString("name"));
120         List fields = config.getList("fields.field.name");
121         assertEquals("Wrong number of fields", TABLE_FIELDS[0].length, fields
122                 .size());
123         for (int i = 0; i < TABLE_FIELDS[0].length; i++)
124         {
125             assertEquals("Wrong field at position " + i, TABLE_FIELDS[0][i],
126                     fields.get(i));
127         }
128     }
129 
130     /***
131      * Tests setting of properties in both the parent and the subnode
132      * configuration and whether the changes are visible to each other.
133      */
134     public void testSetProperty()
135     {
136         setUpSubnodeConfig();
137         config.setProperty(null, "testTable");
138         config.setProperty("name", TABLE_NAMES[0] + "_tested");
139         assertEquals("Root value was not set", "testTable", parent
140                 .getString("tables.table(0)"));
141         assertEquals("Table name was not changed", TABLE_NAMES[0] + "_tested",
142                 parent.getString("tables.table(0).name"));
143 
144         parent.setProperty("tables.table(0).fields.field(1).name", "testField");
145         assertEquals("Field name was not changed", "testField", config
146                 .getString("fields.field(1).name"));
147     }
148 
149     /***
150      * Tests adding of properties.
151      */
152     public void testAddProperty()
153     {
154         setUpSubnodeConfig();
155         config.addProperty("[@table-type]", "test");
156         assertEquals("parent.createNode() was not called", 1, nodeCounter);
157         assertEquals("Attribute not set", "test", parent
158                 .getString("tables.table(0)[@table-type]"));
159 
160         parent.addProperty("tables.table(0).fields.field(-1).name", "newField");
161         List fields = config.getList("fields.field.name");
162         assertEquals("New field was not added", TABLE_FIELDS[0].length + 1,
163                 fields.size());
164         assertEquals("Wrong last field", "newField", fields
165                 .get(fields.size() - 1));
166     }
167 
168     /***
169      * Tests listing the defined keys.
170      */
171     public void testGetKeys()
172     {
173         setUpSubnodeConfig();
174         Set keys = new HashSet();
175         CollectionUtils.addAll(keys, config.getKeys());
176         assertEquals("Incorrect number of keys", 2, keys.size());
177         assertTrue("Key 1 not contained", keys.contains("name"));
178         assertTrue("Key 2 not contained", keys.contains("fields.field.name"));
179     }
180 
181     /***
182      * Tests setting the exception on missing flag. The subnode config obtains
183      * this flag from its parent.
184      */
185     public void testSetThrowExceptionOnMissing()
186     {
187         parent.setThrowExceptionOnMissing(true);
188         setUpSubnodeConfig();
189         assertTrue("Exception flag not fetchted from parent", config
190                 .isThrowExceptionOnMissing());
191         try
192         {
193             config.getString("non existing key");
194             fail("Could fetch non existing key!");
195         }
196         catch (NoSuchElementException nex)
197         {
198             // ok
199         }
200 
201         config.setThrowExceptionOnMissing(false);
202         assertTrue("Exception flag reset on parent", parent
203                 .isThrowExceptionOnMissing());
204     }
205 
206     /***
207      * Tests handling of the delimiter parsing disabled flag. This is shared
208      * with the parent, too.
209      */
210     public void testSetDelimiterParsingDisabled()
211     {
212         parent.setDelimiterParsingDisabled(true);
213         setUpSubnodeConfig();
214         parent.setDelimiterParsingDisabled(false);
215         assertTrue("Delimiter parsing flag was not received from parent",
216                 config.isDelimiterParsingDisabled());
217         config.addProperty("newProp", "test1,test2,test3");
218         assertEquals("New property was splitted", "test1,test2,test3", parent
219                 .getString("tables.table(0).newProp"));
220         parent.setDelimiterParsingDisabled(true);
221         config.setDelimiterParsingDisabled(false);
222         assertTrue("Delimiter parsing flag was reset on parent", parent
223                 .isDelimiterParsingDisabled());
224     }
225 
226     /***
227      * Tests manipulating the list delimiter. This piece of data is derived from
228      * the parent.
229      */
230     public void testSetListDelimiter()
231     {
232         parent.setListDelimiter('/');
233         setUpSubnodeConfig();
234         parent.setListDelimiter(';');
235         assertEquals("List delimiter not obtained from parent", '/', config
236                 .getListDelimiter());
237         config.addProperty("newProp", "test1,test2/test3");
238         assertEquals("List was incorrectly splitted", "test1,test2", parent
239                 .getString("tables.table(0).newProp"));
240         config.setListDelimiter(',');
241         assertEquals("List delimiter changed on parent", ';', parent
242                 .getListDelimiter());
243     }
244 
245     /***
246      * Tests changing the expression engine.
247      */
248     public void testSetExpressionEngine()
249     {
250         parent.setExpressionEngine(new XPathExpressionEngine());
251         setUpSubnodeConfig();
252         assertEquals("Wrong field name", TABLE_FIELDS[0][1], config
253                 .getString("fields/field[2]/name"));
254         Set keys = new HashSet();
255         CollectionUtils.addAll(keys, config.getKeys());
256         assertEquals("Wrong number of keys", 2, keys.size());
257         assertTrue("Key 1 not contained", keys.contains("name"));
258         assertTrue("Key 2 not contained", keys.contains("fields/field/name"));
259         config.setExpressionEngine(null);
260         assertTrue("Expression engine reset on parent", parent
261                 .getExpressionEngine() instanceof XPathExpressionEngine);
262     }
263 
264     /***
265      * Tests the configurationAt() method.
266      */
267     public void testConfiguarationAt()
268     {
269         setUpSubnodeConfig();
270         SubnodeConfiguration sub2 = (SubnodeConfiguration) config
271                 .configurationAt("fields.field(1)");
272         assertEquals("Wrong value of property", TABLE_FIELDS[0][1], sub2
273                 .getString("name"));
274         assertEquals("Wrong parent", config.getParent(), sub2.getParent());
275     }
276 
277     /***
278      * Tests interpolation features. The subnode config should use its parent
279      * for interpolation.
280      */
281     public void testInterpolation()
282     {
283         parent.addProperty("tablespaces.tablespace.name", "default");
284         parent.addProperty("tablespaces.tablespace(-1).name", "test");
285         parent.addProperty("tables.table(0).tablespace",
286                 "${tablespaces.tablespace(0).name}");
287         assertEquals("Wrong interpolated tablespace", "default", parent
288                 .getString("tables.table(0).tablespace"));
289 
290         setUpSubnodeConfig();
291         assertEquals("Wrong interpolated tablespace in subnode", "default",
292                 config.getString("tablespace"));
293     }
294 
295     /***
296      * Initializes the parent configuration. This method creates the typical
297      * structure of tables and fields nodes.
298      *
299      * @return the parent configuration
300      */
301     protected HierarchicalConfiguration setUpParentConfig()
302     {
303         HierarchicalConfiguration conf = new HierarchicalConfiguration()
304         {
305             // Provide a special implementation of createNode() to check
306             // if it is called by the subnode config
307             protected Node createNode(String name)
308             {
309                 nodeCounter++;
310                 return super.createNode(name);
311             }
312         };
313         for (int i = 0; i < TABLE_NAMES.length; i++)
314         {
315             conf.addProperty("tables.table(-1).name", TABLE_NAMES[i]);
316             for (int j = 0; j < TABLE_FIELDS[i].length; j++)
317             {
318                 conf.addProperty("tables.table.fields.field(-1).name",
319                         TABLE_FIELDS[i][j]);
320             }
321         }
322         return conf;
323     }
324 
325     /***
326      * Returns the root node for the subnode config. This method returns the
327      * first table node.
328      *
329      * @param conf the parent config
330      * @return the root node for the subnode config
331      */
332     protected ConfigurationNode getSubnodeRoot(HierarchicalConfiguration conf)
333     {
334         ConfigurationNode root = conf.getRoot();
335         return root.getChild(0).getChild(0);
336     }
337 
338     /***
339      * Performs a standard initialization of the subnode config to test.
340      */
341     protected void setUpSubnodeConfig()
342     {
343         config = new SubnodeConfiguration(parent, getSubnodeRoot(parent));
344     }
345 }