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