1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.configuration;
18  
19  import java.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.NoSuchElementException;
26  import java.util.Properties;
27  import java.util.StringTokenizer;
28  
29  import junit.framework.TestCase;
30  import junitx.framework.ObjectAssert;
31  
32  /***
33   * Tests some basic functions of the BaseConfiguration class. Missing keys will
34   * throw Exceptions
35   *
36   * @version $Id: TestBaseConfiguration.java 158491 2005-03-21 17:58:56Z ebourg $
37   */
38  public class TestBaseConfiguration extends TestCase
39  {
40      protected BaseConfiguration config = null;
41  
42      protected static Class missingElementException = NoSuchElementException.class;
43      protected static Class incompatibleElementException = ConversionException.class;
44  
45      protected void setUp() throws Exception
46      {
47          config = new BaseConfiguration();
48          config.setThrowExceptionOnMissing(true);
49      }
50  
51      public void testThrowExceptionOnMissing()
52      {
53          assertTrue("Throw Exception Property is not set!", config.isThrowExceptionOnMissing());
54      }
55  
56      public void testGetProperty()
57      {
58          /* should be empty and return null */
59          assertEquals("This returns null", config.getProperty("foo"), null);
60  
61          /* add a real value, and get it two different ways */
62          config.setProperty("number", "1");
63          assertEquals("This returns '1'", config.getProperty("number"), "1");
64          assertEquals("This returns '1'", config.getString("number"), "1");
65      }
66  
67      public void testGetByte()
68      {
69          config.setProperty("number", "1");
70          byte oneB = 1;
71          byte twoB = 2;
72          assertEquals("This returns 1(byte)", oneB, config.getByte("number"));
73          assertEquals("This returns 1(byte)", oneB, config.getByte("number", twoB));
74          assertEquals("This returns 2(default byte)", twoB, config.getByte("numberNotInConfig", twoB));
75          assertEquals("This returns 1(Byte)", new Byte(oneB), config.getByte("number", new Byte("2")));
76  
77          // missing key without default value
78          Throwable t = null;
79          try
80          {
81              config.getByte("numberNotInConfig");
82          }
83          catch (Throwable T)
84          {
85              t = T;
86          }
87          assertNotNull("No exception thrown for missing keys", t);
88          ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
89  
90          // existing key with an incompatible value
91          config.setProperty("test.empty", "");
92          t = null;
93          try
94          {
95              config.getByte("test.empty");
96          }
97          catch (Throwable T)
98          {
99              t = T;
100         }
101         assertNotNull("No exception thrown for incompatible values", t);
102         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
103     }
104 
105     public void testGetShort()
106     {
107         config.setProperty("numberS", "1");
108         short oneS = 1;
109         short twoS = 2;
110         assertEquals("This returns 1(short)", oneS, config.getShort("numberS"));
111         assertEquals("This returns 1(short)", oneS, config.getShort("numberS", twoS));
112         assertEquals("This returns 2(default short)", twoS, config.getShort("numberNotInConfig", twoS));
113         assertEquals("This returns 1(Short)", new Short(oneS), config.getShort("numberS", new Short("2")));
114 
115         // missing key without default value
116         Throwable t = null;
117         try
118         {
119             config.getShort("numberNotInConfig");
120         }
121         catch (Throwable T)
122         {
123             t = T;
124         }
125         assertNotNull("No exception thrown for missing keys", t);
126         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
127 
128         // existing key with an incompatible value
129         config.setProperty("test.empty", "");
130         t = null;
131         try
132         {
133             config.getShort("test.empty");
134         }
135         catch (Throwable T)
136         {
137             t = T;
138         }
139         assertNotNull("No exception thrown for incompatible values", t);
140         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
141     }
142 
143     public void testGetLong()
144     {
145         config.setProperty("numberL", "1");
146         long oneL = 1;
147         long twoL = 2;
148         assertEquals("This returns 1(long)", oneL, config.getLong("numberL"));
149         assertEquals("This returns 1(long)", oneL, config.getLong("numberL", twoL));
150         assertEquals("This returns 2(default long)", twoL, config.getLong("numberNotInConfig", twoL));
151         assertEquals("This returns 1(Long)", new Long(oneL), config.getLong("numberL", new Long("2")));
152 
153         // missing key without default value
154         Throwable t = null;
155         try
156         {
157             config.getLong("numberNotInConfig");
158         }
159         catch (Throwable T)
160         {
161             t = T;
162         }
163         assertNotNull("No exception thrown for missing keys", t);
164         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
165 
166         // existing key with an incompatible value
167         config.setProperty("test.empty", "");
168         t = null;
169         try
170         {
171             config.getLong("test.empty");
172         }
173         catch (Throwable T)
174         {
175             t = T;
176         }
177         assertNotNull("No exception thrown for incompatible values", t);
178         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
179     }
180 
181     public void testGetFloat()
182     {
183         config.setProperty("numberF", "1.0");
184         float oneF = 1;
185         float twoF = 2;
186         assertEquals("This returns 1(float)", oneF, config.getFloat("numberF"), 0);
187         assertEquals("This returns 1(float)", oneF, config.getFloat("numberF", twoF), 0);
188         assertEquals("This returns 2(default float)", twoF, config.getFloat("numberNotInConfig", twoF), 0);
189         assertEquals("This returns 1(Float)", new Float(oneF), config.getFloat("numberF", new Float("2")));
190 
191         // missing key without default value
192         Throwable t = null;
193         try
194         {
195             config.getFloat("numberNotInConfig");
196         }
197         catch (Throwable T)
198         {
199             t = T;
200         }
201         assertNotNull("No exception thrown for missing keys", t);
202         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
203 
204         // existing key with an incompatible value
205         config.setProperty("test.empty", "");
206         t = null;
207         try
208         {
209             config.getFloat("test.empty");
210         }
211         catch (Throwable T)
212         {
213             t = T;
214         }
215         assertNotNull("No exception thrown for incompatible values", t);
216         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
217     }
218 
219     public void testGetDouble()
220     {
221         config.setProperty("numberD", "1.0");
222         double oneD = 1;
223         double twoD = 2;
224         assertEquals("This returns 1(double)", oneD, config.getDouble("numberD"), 0);
225         assertEquals("This returns 1(double)", oneD, config.getDouble("numberD", twoD), 0);
226         assertEquals("This returns 2(default double)", twoD, config.getDouble("numberNotInConfig", twoD), 0);
227         assertEquals("This returns 1(Double)", new Double(oneD), config.getDouble("numberD", new Double("2")));
228 
229         // missing key without default value
230         Throwable t = null;
231         try
232         {
233             config.getDouble("numberNotInConfig");
234         }
235         catch (Throwable T)
236         {
237             t = T;
238         }
239         assertNotNull("No exception thrown for missing keys", t);
240         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
241 
242         // existing key with an incompatible value
243         config.setProperty("test.empty", "");
244         t = null;
245         try
246         {
247             config.getDouble("test.empty");
248         }
249         catch (Throwable T)
250         {
251             t = T;
252         }
253         assertNotNull("No exception thrown for incompatible values", t);
254         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
255     }
256 
257     public void testGetBigDecimal()
258     {
259         config.setProperty("numberBigD", "123.456");
260         BigDecimal number = new BigDecimal("123.456");
261         BigDecimal defaultValue = new BigDecimal("654.321");
262 
263         assertEquals("Existing key", number, config.getBigDecimal("numberBigD"));
264         assertEquals("Existing key with default value", number, config.getBigDecimal("numberBigD", defaultValue));
265         assertEquals("Missing key with default value", defaultValue, config.getBigDecimal("numberNotInConfig", defaultValue));
266 
267         // missing key without default value
268         Throwable t = null;
269         try
270         {
271             config.getBigDecimal("numberNotInConfig");
272         }
273         catch (Throwable T)
274         {
275             t = T;
276         }
277         assertNotNull("No exception thrown for missing keys", t);
278         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
279 
280         // existing key with an incompatible value
281         config.setProperty("test.empty", "");
282         t = null;
283         try
284         {
285             config.getBigDecimal("test.empty");
286         }
287         catch (Throwable T)
288         {
289             t = T;
290         }
291         assertNotNull("No exception thrown for incompatible values", t);
292         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
293     }
294 
295     public void testGetBigInteger()
296     {
297         config.setProperty("numberBigI", "1234567890");
298         BigInteger number = new BigInteger("1234567890");
299         BigInteger defaultValue = new BigInteger("654321");
300 
301         assertEquals("Existing key", number, config.getBigInteger("numberBigI"));
302         assertEquals("Existing key with default value", number, config.getBigInteger("numberBigI", defaultValue));
303         assertEquals("Missing key with default value", defaultValue, config.getBigInteger("numberNotInConfig", defaultValue));
304 
305         // missing key without default value
306         Throwable t = null;
307         try
308         {
309             config.getBigInteger("numberNotInConfig");
310         }
311         catch (Throwable T)
312         {
313             t = T;
314         }
315         assertNotNull("No exception thrown for missing keys", t);
316         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
317 
318         // existing key with an incompatible value
319         config.setProperty("test.empty", "");
320         t = null;
321         try
322         {
323             config.getBigInteger("test.empty");
324         }
325         catch (Throwable T)
326         {
327             t = T;
328         }
329         assertNotNull("No exception thrown for incompatible values", t);
330         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
331     }
332 
333     public void testGetString()
334     {
335         config.setProperty("testString", "The quick brown fox");
336         String string = new String("The quick brown fox");
337         String defaultValue = new String("jumps over the lazy dog");
338 
339         assertEquals("Existing key", string, config.getString("testString"));
340         assertEquals("Existing key with default value", string, config.getString("testString", defaultValue));
341         assertEquals("Missing key with default value", defaultValue, config.getString("stringNotInConfig", defaultValue));
342 
343         // missing key without default value
344         Throwable t = null;
345         try
346         {
347             config.getString("stringNotInConfig");
348         }
349         catch (Throwable T)
350         {
351             t = T;
352         }
353         assertNotNull("No exception thrown for missing keys", t);
354         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
355     }
356 
357     public void testGetBoolean()
358     {
359         config.setProperty("boolA", Boolean.TRUE);
360         boolean boolT = true, boolF = false;
361         assertEquals("This returns true", boolT, config.getBoolean("boolA"));
362         assertEquals("This returns true, not the default", boolT, config.getBoolean("boolA", boolF));
363         assertEquals("This returns false(default)", boolF, config.getBoolean("boolNotInConfig", boolF));
364         assertEquals("This returns true(Boolean)", new Boolean(boolT), config.getBoolean("boolA", new Boolean(boolF)));
365 
366         // missing key without default value
367         Throwable t = null;
368         try
369         {
370             config.getBoolean("numberNotInConfig");
371         }
372         catch (Throwable T)
373         {
374             t = T;
375         }
376         assertNotNull("No exception thrown for missing keys", t);
377         ObjectAssert.assertInstanceOf("Exception thrown for missing keys", missingElementException, t);
378 
379         // existing key with an incompatible value
380         config.setProperty("test.empty", "");
381         t = null;
382         try
383         {
384             config.getBoolean("test.empty");
385         }
386         catch (Throwable T)
387         {
388             t = T;
389         }
390         assertNotNull("No exception thrown for incompatible values", t);
391         ObjectAssert.assertInstanceOf("Exception thrown for incompatible values", incompatibleElementException, t);
392     }
393 
394     public void testGetList()
395     {
396         config.addProperty("number", "1");
397         config.addProperty("number", "2");
398         List list = config.getList("number");
399         assertNotNull("The list is null", list);
400         assertEquals("List size", 2, list.size());
401         assertTrue("The number 1 is missing from the list", list.contains("1"));
402         assertTrue("The number 2 is missing from the list", list.contains("2"));
403 
404         /*
405          *  now test dan's new fix where we get the first scalar
406          *  when we access a list valued property
407          */
408         try
409         {
410             config.getString("number");
411         }
412         catch (NoSuchElementException nsse)
413         {
414             fail("Should return a string");
415         }
416     }
417 
418     public void testCommaSeparatedString()
419     {
420         String prop = "hey, that's a test";
421         config.setProperty("prop.string", prop);
422         try
423         {
424             config.getList("prop.string");
425         }
426         catch (NoSuchElementException nsse)
427         {
428             fail("Should return a list");
429         }
430 
431         String prop2 = "hey//, that's a test";
432         config.clearProperty("prop.string");
433         config.setProperty("prop.string", prop2);
434         try
435         {
436             config.getString("prop.string");
437         }
438         catch (NoSuchElementException nsse)
439         {
440             fail("Should return a list");
441         }
442 
443     }
444     
445     public void testAddProperty() throws Exception
446     {
447         Collection props = new ArrayList();
448         props.add("one");
449         props.add("two,three,four");
450         props.add(new String[] { "5.1", "5.2", "5.3,5.4", "5.5" });
451         props.add("six");
452         config.addProperty("complex.property", props);
453         
454         Object val = config.getProperty("complex.property");
455         assertTrue(val instanceof Collection);
456         Collection col = (Collection) val;
457         assertEquals(10, col.size());
458         
459         props = new ArrayList();
460         props.add("quick");
461         props.add("brown");
462         props.add("fox,jumps");
463         Object[] data = new Object[] {
464                 "The", props, "over,the", "lazy", "dog."
465         };
466         config.setProperty("complex.property", data);
467         val = config.getProperty("complex.property");
468         assertTrue(val instanceof Collection);
469         col = (Collection) val;
470         Iterator it = col.iterator();
471         StringTokenizer tok = new StringTokenizer("The quick brown fox jumps over the lazy dog.", " ");
472         while(tok.hasMoreTokens())
473         {
474             assertTrue(it.hasNext());
475             assertEquals(tok.nextToken(), it.next());
476         }
477         assertFalse(it.hasNext());
478         
479         config.setProperty("complex.property", null);
480         assertFalse(config.containsKey("complex.property"));
481     }
482 
483     public void testPropertyAccess()
484     {
485         config.clearProperty("prop.properties");
486         config.setProperty("prop.properties", "");
487         assertEquals(
488                 "This returns an empty Properties object",
489                 config.getProperties("prop.properties"),
490                 new Properties());
491         config.clearProperty("prop.properties");
492         config.setProperty("prop.properties", "foo=bar, baz=moo, seal=clubber");
493 
494         Properties p = new Properties();
495         p.setProperty("foo", "bar");
496         p.setProperty("baz", "moo");
497         p.setProperty("seal", "clubber");
498         assertEquals(
499                 "This returns a filled in Properties object",
500                 config.getProperties("prop.properties"),
501                 p);
502     }
503 
504     public void testSubset()
505     {
506         /*
507          * test subset : assure we don't reprocess the data elements
508          * when generating the subset
509          */
510 
511         String prop = "hey, that's a test";
512         String prop2 = "hey//, that's a test";
513         config.setProperty("prop.string", prop2);
514         config.setProperty("property.string", "hello");
515 
516         Configuration subEprop = config.subset("prop");
517 
518         assertEquals(
519                 "Returns the full string",
520                 prop,
521                 subEprop.getString("string"));
522         try
523         {
524             subEprop.getString("string");
525         }
526         catch (NoSuchElementException nsse)
527         {
528             fail("Should return a string");
529         }
530         try
531         {
532             subEprop.getList("string");
533         }
534         catch (NoSuchElementException nsse)
535         {
536             fail("Should return a list");
537         }
538 
539         Iterator it = subEprop.getKeys();
540         it.next();
541         assertFalse(it.hasNext());
542 
543         subEprop = config.subset("prop.");
544         it = subEprop.getKeys();
545         assertFalse(it.hasNext());
546     }
547 
548     public void testInterpolation() throws Exception
549     {
550         config.setProperty("applicationRoot", "/home/applicationRoot");
551         config.setProperty("db", "${applicationRoot}/db/hypersonic");
552         String unInterpolatedValue = "${applicationRoot2}/db/hypersonic";
553         config.setProperty("dbFailedInterpolate", unInterpolatedValue);
554         String dbProp = "/home/applicationRoot/db/hypersonic";
555 
556         //construct a new config, using config as the defaults config for it.
557         BaseConfiguration superProp = config;
558 
559         assertEquals(
560                 "Checking interpolated variable", dbProp,
561                 superProp.getString("db"));
562         assertEquals(
563                 "lookup fails, leave variable as is",
564                 superProp.getString("dbFailedInterpolate"),
565                 unInterpolatedValue);
566 
567         superProp.setProperty("arrayInt", "${applicationRoot}/1");
568         String[] arrayInt = superProp.getStringArray("arrayInt");
569         assertEquals(
570                 "check first entry was interpolated",
571                 "/home/applicationRoot/1",
572                 arrayInt[0]);
573     }
574 
575     public void testMultipleInterpolation() throws Exception
576     {
577         config.setProperty("test.base-level", "/base-level");
578         config.setProperty("test.first-level", "${test.base-level}/first-level");
579         config.setProperty(
580                 "test.second-level",
581                 "${test.first-level}/second-level");
582         config.setProperty(
583                 "test.third-level",
584                 "${test.second-level}/third-level");
585 
586         String expectedValue =
587                 "/base-level/first-level/second-level/third-level";
588 
589         assertEquals(config.getString("test.third-level"), expectedValue);
590     }
591 
592     public void testInterpolationLoop() throws Exception
593     {
594         config.setProperty("test.a", "${test.b}");
595         config.setProperty("test.b", "${test.a}");
596 
597         try
598         {
599             config.getString("test.a");
600         }
601         catch (IllegalStateException e)
602         {
603             return;
604         }
605 
606         fail("IllegalStateException should have been thrown for looped property references");
607     }
608 
609     public void testGetHexadecimalValue()
610     {
611         config.setProperty("number", "0xFF");
612         assertEquals("byte value", (byte) 0xFF, config.getByte("number"));
613 
614         config.setProperty("number", "0xFFFF");
615         assertEquals("short value", (short) 0xFFFF, config.getShort("number"));
616 
617         config.setProperty("number", "0xFFFFFFFF");
618         assertEquals("int value", 0xFFFFFFFF, config.getInt("number"));
619 
620         config.setProperty("number", "0xFFFFFFFFFFFFFFFF");
621         assertEquals("long value", 0xFFFFFFFFFFFFFFFFL, config.getLong("number"));
622 
623         assertEquals("long value", 0xFFFFFFFFFFFFFFFFL, config.getBigInteger("number").longValue());
624     }
625 
626     public void testResolveContainerStore()
627     {
628         AbstractConfiguration config = new BaseConfiguration();
629 
630         // array of objects
631         config.addPropertyDirect("array", new String[] { "foo", "bar" });
632 
633         assertEquals("first element of the 'array' property", "foo", config.resolveContainerStore("array"));
634 
635         // list of objects
636         List list = new ArrayList();
637         list.add("foo");
638         list.add("bar");
639         config.addPropertyDirect("list", list);
640 
641         assertEquals("first element of the 'list' property", "foo", config.resolveContainerStore("list"));
642 
643         // arrays of primitives
644         config.addPropertyDirect("array.boolean", new boolean[] { true, false });
645         assertEquals("first element of the 'array.boolean' property", true, config.getBoolean("array.boolean"));
646 
647         config.addPropertyDirect("array.byte", new byte[] { 1, 2 });
648         assertEquals("first element of the 'array.byte' property", 1, config.getByte("array.byte"));
649 
650         config.addPropertyDirect("array.short", new short[] { 1, 2 });
651         assertEquals("first element of the 'array.short' property", 1, config.getShort("array.short"));
652 
653         config.addPropertyDirect("array.int", new int[] { 1, 2 });
654         assertEquals("first element of the 'array.int' property", 1, config.getInt("array.int"));
655 
656         config.addPropertyDirect("array.long", new long[] { 1, 2 });
657         assertEquals("first element of the 'array.long' property", 1, config.getLong("array.long"));
658 
659         config.addPropertyDirect("array.float", new float[] { 1, 2 });
660         assertEquals("first element of the 'array.float' property", 1, config.getFloat("array.float"), 0);
661 
662         config.addPropertyDirect("array.double", new double[] { 1, 2 });
663         assertEquals("first element of the 'array.double' property", 1, config.getDouble("array.double"), 0);
664     }
665 
666 }