KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > ExtendedProperties


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 package org.apache.commons.collections;
17
18 import java.io.File JavaDoc;
19 import java.io.FileInputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.InputStreamReader JavaDoc;
23 import java.io.LineNumberReader JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.io.Reader JavaDoc;
27 import java.io.UnsupportedEncodingException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.NoSuchElementException JavaDoc;
34 import java.util.Properties JavaDoc;
35 import java.util.StringTokenizer JavaDoc;
36 import java.util.Vector JavaDoc;
37
38 /**
39  * This class extends normal Java properties by adding the possibility
40  * to use the same key many times concatenating the value strings
41  * instead of overwriting them.
42  * <p>
43  * <b>Please consider using the <code>PropertiesConfiguration</code> class in
44  * Commons-Configuration as soon as it is released.</b>
45  * <p>
46  * The Extended Properties syntax is explained here:
47  *
48  * <ul>
49  * <li>
50  * Each property has the syntax <code>key = value</code>
51  * </li>
52  * <li>
53  * The <i>key</i> may use any character but the equal sign '='.
54  * </li>
55  * <li>
56  * <i>value</i> may be separated on different lines if a backslash
57  * is placed at the end of the line that continues below.
58  * </li>
59  * <li>
60  * If <i>value</i> is a list of strings, each token is separated
61  * by a comma ','.
62  * </li>
63  * <li>
64  * Commas in each token are escaped placing a backslash right before
65  * the comma.
66  * </li>
67  * <li>
68  * Backslashes are escaped by using two consecutive backslashes i.e. \\
69  * </li>
70  * <li>
71  * If a <i>key</i> is used more than once, the values are appended
72  * like if they were on the same line separated with commas.
73  * </li>
74  * <li>
75  * Blank lines and lines starting with character '#' are skipped.
76  * </li>
77  * <li>
78  * If a property is named "include" (or whatever is defined by
79  * setInclude() and getInclude() and the value of that property is
80  * the full path to a file on disk, that file will be included into
81  * the ConfigurationsRepository. You can also pull in files relative
82  * to the parent configuration file. So if you have something
83  * like the following:
84  *
85  * include = additional.properties
86  *
87  * Then "additional.properties" is expected to be in the same
88  * directory as the parent configuration file.
89  *
90  * Duplicate name values will be replaced, so be careful.
91  *
92  * </li>
93  * </ul>
94  *
95  * <p>Here is an example of a valid extended properties file:
96  *
97  * <p><pre>
98  * # lines starting with # are comments
99  *
100  * # This is the simplest property
101  * key = value
102  *
103  * # A long property may be separated on multiple lines
104  * longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
105  * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
106  *
107  * # This is a property with many tokens
108  * tokens_on_a_line = first token, second token
109  *
110  * # This sequence generates exactly the same result
111  * tokens_on_multiple_lines = first token
112  * tokens_on_multiple_lines = second token
113  *
114  * # commas may be escaped in tokens
115  * commas.escaped = Hi\, what'up?
116  * </pre>
117  *
118  * <p><b>NOTE</b>: this class has <b>not</b> been written for
119  * performance nor low memory usage. In fact, it's way slower than it
120  * could be and generates too much memory garbage. But since
121  * performance is not an issue during intialization (and there is not
122  * much time to improve it), I wrote it this way. If you don't like
123  * it, go ahead and tune it up!
124  *
125  * @since Commons Collections 1.0
126  * @version $Revision: 1.23 $ $Date: 2004/06/21 23:39:25 $
127  *
128  * @author <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
129  * @author <a HREF="mailto:jon@latchkey.com">Jon S. Stevens</a>
130  * @author <a HREF="mailto:daveb@miceda-data">Dave Bryson</a>
131  * @author <a HREF="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
132  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
133  * @author <a HREF="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
134  * @author <a HREF="mailto:kjohnson@transparent.com">Kent Johnson</a>
135  * @author <a HREF="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
136  * @author <a HREF="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
137  * @author Janek Bogucki
138  * @author Mohan Kishore
139  * @author Stephen Colebourne
140  */

141 public class ExtendedProperties extends Hashtable JavaDoc {
142     
143     /**
144      * Default configurations repository.
145      */

146     private ExtendedProperties defaults;
147
148     /**
149      * The file connected to this repository (holding comments and
150      * such).
151      *
152      * @serial
153      */

154     protected String JavaDoc file;
155
156     /**
157      * Base path of the configuration file used to create
158      * this ExtendedProperties object.
159      */

160     protected String JavaDoc basePath;
161
162     /**
163      * File separator.
164      */

165     protected String JavaDoc fileSeparator = System.getProperty("file.separator");
166
167     /**
168      * Has this configuration been intialized.
169      */

170     protected boolean isInitialized = false;
171
172     /**
173      * This is the name of the property that can point to other
174      * properties file for including other properties files.
175      */

176     protected static String JavaDoc include = "include";
177
178     /**
179      * These are the keys in the order they listed
180      * in the configuration file. This is useful when
181      * you wish to perform operations with configuration
182      * information in a particular order.
183      */

184     protected ArrayList JavaDoc keysAsListed = new ArrayList JavaDoc();
185
186     protected final static String JavaDoc START_TOKEN="${";
187     protected final static String JavaDoc END_TOKEN="}";
188
189
190     /**
191      * Interpolate key names to handle ${key} stuff
192      *
193      * @param base string to interpolate
194      * @return returns the key name with the ${key} substituted
195      */

196     protected String JavaDoc interpolate(String JavaDoc base) {
197         // COPIED from [configuration] 2003-12-29
198
return (interpolateHelper(base, null));
199     }
200
201     /**
202      * Recursive handler for multiple levels of interpolation.
203      *
204      * When called the first time, priorVariables should be null.
205      *
206      * @param base string with the ${key} variables
207      * @param priorVariables serves two purposes: to allow checking for
208      * loops, and creating a meaningful exception message should a loop
209      * occur. It's 0'th element will be set to the value of base from
210      * the first call. All subsequent interpolated variables are added
211      * afterward.
212      *
213      * @return the string with the interpolation taken care of
214      */

215     protected String JavaDoc interpolateHelper(String JavaDoc base, List JavaDoc priorVariables) {
216         // COPIED from [configuration] 2003-12-29
217
if (base == null) {
218             return null;
219         }
220
221         // on the first call initialize priorVariables
222
// and add base as the first element
223
if (priorVariables == null) {
224             priorVariables = new ArrayList JavaDoc();
225             priorVariables.add(base);
226         }
227
228         int begin = -1;
229         int end = -1;
230         int prec = 0 - END_TOKEN.length();
231         String JavaDoc variable = null;
232         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
233
234         // FIXME: we should probably allow the escaping of the start token
235
while (((begin = base.indexOf(START_TOKEN, prec + END_TOKEN.length())) > -1)
236             && ((end = base.indexOf(END_TOKEN, begin)) > -1)) {
237             result.append(base.substring(prec + END_TOKEN.length(), begin));
238             variable = base.substring(begin + START_TOKEN.length(), end);
239
240             // if we've got a loop, create a useful exception message and throw
241
if (priorVariables.contains(variable)) {
242                 String JavaDoc initialBase = priorVariables.remove(0).toString();
243                 priorVariables.add(variable);
244                 StringBuffer JavaDoc priorVariableSb = new StringBuffer JavaDoc();
245
246                 // create a nice trace of interpolated variables like so:
247
// var1->var2->var3
248
for (Iterator JavaDoc it = priorVariables.iterator(); it.hasNext();) {
249                     priorVariableSb.append(it.next());
250                     if (it.hasNext()) {
251                         priorVariableSb.append("->");
252                     }
253                 }
254
255                 throw new IllegalStateException JavaDoc(
256                     "infinite loop in property interpolation of " + initialBase + ": " + priorVariableSb.toString());
257             }
258             // otherwise, add this variable to the interpolation list.
259
else {
260                 priorVariables.add(variable);
261             }
262
263             //QUESTION: getProperty or getPropertyDirect
264
Object JavaDoc value = getProperty(variable);
265             if (value != null) {
266                 result.append(interpolateHelper(value.toString(), priorVariables));
267
268                 // pop the interpolated variable off the stack
269
// this maintains priorVariables correctness for
270
// properties with multiple interpolations, e.g.
271
// prop.name=${some.other.prop1}/blahblah/${some.other.prop2}
272
priorVariables.remove(priorVariables.size() - 1);
273             } else if (defaults != null && defaults.getString(variable, null) != null) {
274                 result.append(defaults.getString(variable));
275             } else {
276                 //variable not defined - so put it back in the value
277
result.append(START_TOKEN).append(variable).append(END_TOKEN);
278             }
279             prec = end;
280         }
281         result.append(base.substring(prec + END_TOKEN.length(), base.length()));
282
283         return result.toString();
284     }
285     
286     /**
287      * Inserts a backslash before every comma and backslash.
288      */

289     private static String JavaDoc escape(String JavaDoc s) {
290         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(s);
291         for (int i = 0; i < buf.length(); i++) {
292             char c = buf.charAt(i);
293             if (c == ',' || c == '\\') {
294                 buf.insert(i, '\\');
295                 i++;
296             }
297         }
298         return buf.toString();
299     }
300     
301     /**
302      * Removes a backslash from every pair of backslashes.
303      */

304     private static String JavaDoc unescape(String JavaDoc s) {
305         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(s);
306         for (int i = 0; i < buf.length() - 1; i++) {
307             char c1 = buf.charAt(i);
308             char c2 = buf.charAt(i + 1);
309             if (c1 == '\\' && c2 == '\\') {
310                 buf.deleteCharAt(i);
311             }
312         }
313         return buf.toString();
314     }
315
316     /**
317      * Counts the number of successive times 'ch' appears in the
318      * 'line' before the position indicated by the 'index'.
319      */

320     private static int countPreceding(String JavaDoc line, int index, char ch) {
321         int i;
322         for (i = index - 1; i >= 0; i--) {
323             if (line.charAt(i) != ch) {
324                 break;
325             }
326         }
327         return index - 1 - i;
328     }
329
330     /**
331      * Checks if the line ends with odd number of backslashes
332      */

333     private static boolean endsWithSlash(String JavaDoc line) {
334         if (!line.endsWith("\\")) {
335             return false;
336         }
337         return (countPreceding(line, line.length() - 1, '\\') % 2 == 0);
338     }
339
340     /**
341      * This class is used to read properties lines. These lines do
342      * not terminate with new-line chars but rather when there is no
343      * backslash sign a the end of the line. This is used to
344      * concatenate multiple lines for readability.
345      */

346     static class PropertiesReader extends LineNumberReader JavaDoc {
347         /**
348          * Constructor.
349          *
350          * @param reader A Reader.
351          */

352         public PropertiesReader(Reader JavaDoc reader) {
353             super(reader);
354         }
355
356         /**
357          * Read a property.
358          *
359          * @return a String property
360          * @throws IOException if there is difficulty reading the source.
361          */

362         public String JavaDoc readProperty() throws IOException JavaDoc {
363             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
364
365             try {
366                 while (true) {
367                     String JavaDoc line = readLine().trim();
368                     if ((line.length() != 0) && (line.charAt(0) != '#')) {
369                         if (endsWithSlash(line)) {
370                             line = line.substring(0, line.length() - 1);
371                             buffer.append(line);
372                         } else {
373                             buffer.append(line);
374                             break;
375                         }
376                     }
377                 }
378             } catch (NullPointerException JavaDoc ex) {
379                 return null;
380             }
381
382             return buffer.toString();
383         }
384     }
385
386     /**
387      * This class divides into tokens a property value. Token
388      * separator is "," but commas into the property value are escaped
389      * using the backslash in front.
390      */

391     static class PropertiesTokenizer extends StringTokenizer JavaDoc {
392         /**
393          * The property delimiter used while parsing (a comma).
394          */

395         static final String JavaDoc DELIMITER = ",";
396
397         /**
398          * Constructor.
399          *
400          * @param string A String.
401          */

402         public PropertiesTokenizer(String JavaDoc string) {
403             super(string, DELIMITER);
404         }
405
406         /**
407          * Check whether the object has more tokens.
408          *
409          * @return True if the object has more tokens.
410          */

411         public boolean hasMoreTokens() {
412             return super.hasMoreTokens();
413         }
414
415         /**
416          * Get next token.
417          *
418          * @return A String.
419          */

420         public String JavaDoc nextToken() {
421             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
422
423             while (hasMoreTokens()) {
424                 String JavaDoc token = super.nextToken();
425                 if (endsWithSlash(token)) {
426                     buffer.append(token.substring(0, token.length() - 1));
427                     buffer.append(DELIMITER);
428                 } else {
429                     buffer.append(token);
430                     break;
431                 }
432             }
433
434             return buffer.toString().trim();
435         }
436     }
437
438     /**
439      * Creates an empty extended properties object.
440      */

441     public ExtendedProperties() {
442         super();
443     }
444
445     /**
446      * Creates and loads the extended properties from the specified file.
447      *
448      * @param file the filename to load
449      * @throws IOException if a file error occurs
450      */

451     public ExtendedProperties(String JavaDoc file) throws IOException JavaDoc {
452         this(file, null);
453     }
454
455     /**
456      * Creates and loads the extended properties from the specified file.
457      *
458      * @param file the filename to load
459      * @param defaultFile a second filename to load default values from
460      * @throws IOException if a file error occurs
461      */

462     public ExtendedProperties(String JavaDoc file, String JavaDoc defaultFile) throws IOException JavaDoc {
463         this.file = file;
464
465         basePath = new File JavaDoc(file).getAbsolutePath();
466         basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1);
467
468         FileInputStream JavaDoc in = null;
469         try {
470             in = new FileInputStream JavaDoc(file);
471             this.load(in);
472         } finally {
473             try {
474                 if (in != null) {
475                     in.close();
476                 }
477             } catch (IOException JavaDoc ex) {}
478         }
479
480         if (defaultFile != null) {
481             defaults = new ExtendedProperties(defaultFile);
482         }
483     }
484
485     /**
486      * Indicate to client code whether property
487      * resources have been initialized or not.
488      */

489     public boolean isInitialized() {
490         return isInitialized;
491     }
492
493     /**
494      * Gets the property value for including other properties files.
495      * By default it is "include".
496      *
497      * @return A String.
498      */

499     public String JavaDoc getInclude() {
500         return include;
501     }
502
503     /**
504      * Sets the property value for including other properties files.
505      * By default it is "include".
506      *
507      * @param inc A String.
508      */

509     public void setInclude(String JavaDoc inc) {
510         include = inc;
511     }
512
513     /**
514      * Load the properties from the given input stream.
515      *
516      * @param input the InputStream to load from
517      * @throws IOException if an IO error occurs
518      */

519     public void load(InputStream JavaDoc input) throws IOException JavaDoc {
520         load(input, null);
521     }
522
523     /**
524      * Load the properties from the given input stream
525      * and using the specified encoding.
526      *
527      * @param input the InputStream to load from
528      * @param enc the encoding to use
529      * @throws IOException if an IO error occurs
530      */

531     public synchronized void load(InputStream JavaDoc input, String JavaDoc enc) throws IOException JavaDoc {
532         PropertiesReader reader = null;
533         if (enc != null) {
534             try {
535                 reader = new PropertiesReader(new InputStreamReader JavaDoc(input, enc));
536                 
537             } catch (UnsupportedEncodingException JavaDoc ex) {
538                 // Another try coming up....
539
}
540         }
541         
542         if (reader == null) {
543             try {
544                 reader = new PropertiesReader(new InputStreamReader JavaDoc(input, "8859_1"));
545                 
546             } catch (UnsupportedEncodingException JavaDoc ex) {
547                 // ISO8859-1 support is required on java platforms but....
548
// If it's not supported, use the system default encoding
549
reader = new PropertiesReader(new InputStreamReader JavaDoc(input));
550             }
551         }
552
553         try {
554             while (true) {
555                 String JavaDoc line = reader.readProperty();
556                 int equalSign = line.indexOf('=');
557
558                 if (equalSign > 0) {
559                     String JavaDoc key = line.substring(0, equalSign).trim();
560                     String JavaDoc value = line.substring(equalSign + 1).trim();
561
562                     // Configure produces lines like this ... just ignore them
563
if ("".equals(value)) {
564                         continue;
565                     }
566
567                     if (getInclude() != null && key.equalsIgnoreCase(getInclude())) {
568                         // Recursively load properties files.
569
File JavaDoc file = null;
570
571                         if (value.startsWith(fileSeparator)) {
572                             // We have an absolute path so we'll use this
573
file = new File JavaDoc(value);
574                             
575                         } else {
576                             // We have a relative path, and we have two
577
// possible forms here. If we have the "./" form
578
// then just strip that off first before continuing.
579
if (value.startsWith("." + fileSeparator)) {
580                                 value = value.substring(2);
581                             }
582
583                             file = new File JavaDoc(basePath + value);
584                         }
585
586                         if (file != null && file.exists() && file.canRead()) {
587                             load(new FileInputStream JavaDoc(file));
588                         }
589                     } else {
590                         addProperty(key, value);
591                     }
592                 }
593             }
594         } catch (NullPointerException JavaDoc ex) {
595             // Should happen only when EOF is reached.
596
return;
597         } finally {
598             // Loading is initializing
599
isInitialized = true;
600         }
601     }
602
603     /**
604      * Gets a property from the configuration.
605      *
606      * @param key property to retrieve
607      * @return value as object. Will return user value if exists,
608      * if not then default value if exists, otherwise null
609      */

610     public Object JavaDoc getProperty(String JavaDoc key) {
611         // first, try to get from the 'user value' store
612
Object JavaDoc obj = this.get(key);
613
614         if (obj == null) {
615             // if there isn't a value there, get it from the
616
// defaults if we have them
617
if (defaults != null) {
618                 obj = defaults.get(key);
619             }
620         }
621
622         return obj;
623     }
624     
625     /**
626      * Add a property to the configuration. If it already
627      * exists then the value stated here will be added
628      * to the configuration entry. For example, if
629      *
630      * <code>resource.loader = file</code>
631      *
632      * is already present in the configuration and you
633      *
634      * <code>addProperty("resource.loader", "classpath")</code>
635      *
636      * Then you will end up with a Vector like the
637      * following:
638      *
639      * <code>["file", "classpath"]</code>
640      *
641      * @param key the key to add
642      * @param value the value to add
643      */

644     public void addProperty(String JavaDoc key, Object JavaDoc value) {
645         if (value instanceof String JavaDoc) {
646             String JavaDoc str = (String JavaDoc) value;
647             if (str.indexOf(PropertiesTokenizer.DELIMITER) > 0) {
648                 // token contains commas, so must be split apart then added
649
PropertiesTokenizer tokenizer = new PropertiesTokenizer(str);
650                 while (tokenizer.hasMoreTokens()) {
651                     String JavaDoc token = tokenizer.nextToken();
652                     addPropertyInternal(key, unescape(token));
653                 }
654             } else {
655                 // token contains no commas, so can be simply added
656
addPropertyInternal(key, unescape(str));
657             }
658         } else {
659             addPropertyInternal(key, value);
660         }
661
662         // Adding a property connotes initialization
663
isInitialized = true;
664     }
665
666     /**
667      * Adds a key/value pair to the map. This routine does
668      * no magic morphing. It ensures the keylist is maintained
669      *
670      * @param key the key to store at
671      * @param value the decoded object to store
672      */

673     private void addPropertyDirect(String JavaDoc key, Object JavaDoc value) {
674         // safety check
675
if (!containsKey(key)) {
676             keysAsListed.add(key);
677         }
678         put(key, value);
679     }
680
681     /**
682      * Adds a decoded property to the map w/o checking for commas - used
683      * internally when a property has been broken up into
684      * strings that could contain escaped commas to prevent
685      * the inadvertent vectorization.
686      * <p>
687      * Thanks to Leon Messerschmidt for this one.
688      *
689      * @param key the key to store at
690      * @param value the decoded object to store
691      */

692     private void addPropertyInternal(String JavaDoc key, Object JavaDoc value) {
693         Object JavaDoc current = this.get(key);
694
695         if (current instanceof String JavaDoc) {
696             // one object already in map - convert it to a vector
697
Vector JavaDoc v = new Vector JavaDoc(2);
698             v.addElement(current);
699             v.addElement(value);
700             put(key, v);
701             
702         } else if (current instanceof Vector JavaDoc) {
703             // already a vector - just add the new token
704
((Vector JavaDoc) current).addElement(value);
705             
706         } else {
707             // brand new key - store in keysAsListed to retain order
708
if (!containsKey(key)) {
709                 keysAsListed.add(key);
710             }
711             put(key, value);
712         }
713     }
714
715     /**
716      * Set a property, this will replace any previously
717      * set values. Set values is implicitly a call
718      * to clearProperty(key), addProperty(key,value).
719      *
720      * @param key the key to set
721      * @param value the value to set
722      */

723     public void setProperty(String JavaDoc key, Object JavaDoc value) {
724         clearProperty(key);
725         addProperty(key, value);
726     }
727     
728     /**
729      * Save the properties to the given output stream.
730      * <p>
731      * The stream is not closed, but it is flushed.
732      *
733      * @param output an OutputStream, may be null
734      * @param header a textual comment to act as a file header
735      * @throws IOException if an IO error occurs
736      */

737     public synchronized void save(OutputStream JavaDoc output, String JavaDoc header) throws IOException JavaDoc {
738         if (output == null) {
739             return;
740         }
741         PrintWriter JavaDoc theWrtr = new PrintWriter JavaDoc(output);
742         if (header != null) {
743             theWrtr.println(header);
744         }
745         
746         Enumeration JavaDoc theKeys = keys();
747         while (theKeys.hasMoreElements()) {
748             String JavaDoc key = (String JavaDoc) theKeys.nextElement();
749             Object JavaDoc value = get(key);
750             if (value != null) {
751                 if (value instanceof String JavaDoc) {
752                     StringBuffer JavaDoc currentOutput = new StringBuffer JavaDoc();
753                     currentOutput.append(key);
754                     currentOutput.append("=");
755                     currentOutput.append(escape((String JavaDoc) value));
756                     theWrtr.println(currentOutput.toString());
757                     
758                 } else if (value instanceof Vector JavaDoc) {
759                     Vector JavaDoc values = (Vector JavaDoc) value;
760                     Enumeration JavaDoc valuesEnum = values.elements();
761                     while (valuesEnum.hasMoreElements()) {
762                         String JavaDoc currentElement = (String JavaDoc) valuesEnum.nextElement();
763                         StringBuffer JavaDoc currentOutput = new StringBuffer JavaDoc();
764                         currentOutput.append(key);
765                         currentOutput.append("=");
766                         currentOutput.append(escape(currentElement));
767                         theWrtr.println(currentOutput.toString());
768                     }
769                 }
770             }
771             theWrtr.println();
772             theWrtr.flush();
773         }
774     }
775
776     /**
777      * Combines an existing Hashtable with this Hashtable.
778      * <p>
779      * Warning: It will overwrite previous entries without warning.
780      *
781      * @param props the properties to combine
782      */

783     public void combine(ExtendedProperties props) {
784         for (Iterator JavaDoc it = props.getKeys(); it.hasNext();) {
785             String JavaDoc key = (String JavaDoc) it.next();
786             setProperty(key, props.get(key));
787         }
788     }
789     
790     /**
791      * Clear a property in the configuration.
792      *
793      * @param key the property key to remove along with corresponding value
794      */

795     public void clearProperty(String JavaDoc key) {
796         if (containsKey(key)) {
797             // we also need to rebuild the keysAsListed or else
798
// things get *very* confusing
799
for (int i = 0; i < keysAsListed.size(); i++) {
800                 if (( keysAsListed.get(i)).equals(key)) {
801                     keysAsListed.remove(i);
802                     break;
803                 }
804             }
805             remove(key);
806         }
807     }
808
809     /**
810      * Get the list of the keys contained in the configuration
811      * repository.
812      *
813      * @return an Iterator over the keys
814      */

815     public Iterator JavaDoc getKeys() {
816         return keysAsListed.iterator();
817     }
818
819     /**
820      * Get the list of the keys contained in the configuration
821      * repository that match the specified prefix.
822      *
823      * @param prefix the prefix to match
824      * @return an Iterator of keys that match the prefix
825      */

826     public Iterator JavaDoc getKeys(String JavaDoc prefix) {
827         Iterator JavaDoc keys = getKeys();
828         ArrayList JavaDoc matchingKeys = new ArrayList JavaDoc();
829
830         while (keys.hasNext()) {
831             Object JavaDoc key = keys.next();
832
833             if (key instanceof String JavaDoc && ((String JavaDoc) key).startsWith(prefix)) {
834                 matchingKeys.add(key);
835             }
836         }
837         return matchingKeys.iterator();
838     }
839
840     /**
841      * Create an ExtendedProperties object that is a subset
842      * of this one. Take into account duplicate keys
843      * by using the setProperty() in ExtendedProperties.
844      *
845      * @param prefix the prefix to get a subset for
846      * @return a new independent ExtendedProperties
847      */

848     public ExtendedProperties subset(String JavaDoc prefix) {
849         ExtendedProperties c = new ExtendedProperties();
850         Iterator JavaDoc keys = getKeys();
851         boolean validSubset = false;
852
853         while (keys.hasNext()) {
854             Object JavaDoc key = keys.next();
855
856             if (key instanceof String JavaDoc && ((String JavaDoc) key).startsWith(prefix)) {
857                 if (!validSubset) {
858                     validSubset = true;
859                 }
860
861                 /*
862                  * Check to make sure that c.subset(prefix) doesn't
863                  * blow up when there is only a single property
864                  * with the key prefix. This is not a useful
865                  * subset but it is a valid subset.
866                  */

867                 String JavaDoc newKey = null;
868                 if (((String JavaDoc) key).length() == prefix.length()) {
869                     newKey = prefix;
870                 } else {
871                     newKey = ((String JavaDoc) key).substring(prefix.length() + 1);
872                 }
873
874                 /*
875                  * use addPropertyDirect() - this will plug the data as
876                  * is into the Map, but will also do the right thing
877                  * re key accounting
878                  */

879                 c.addPropertyDirect(newKey, get(key));
880             }
881         }
882
883         if (validSubset) {
884             return c;
885         } else {
886             return null;
887         }
888     }
889
890     /**
891      * Display the configuration for debugging purposes to System.out.
892      */

893     public void display() {
894         Iterator JavaDoc i = getKeys();
895
896         while (i.hasNext()) {
897             String JavaDoc key = (String JavaDoc) i.next();
898             Object JavaDoc value = get(key);
899             System.out.println(key + " => " + value);
900         }
901     }
902
903     /**
904      * Get a string associated with the given configuration key.
905      *
906      * @param key The configuration key.
907      * @return The associated string.
908      * @throws ClassCastException is thrown if the key maps to an
909      * object that is not a String.
910      */

911     public String JavaDoc getString(String JavaDoc key) {
912         return getString(key, null);
913     }
914
915     /**
916      * Get a string associated with the given configuration key.
917      *
918      * @param key The configuration key.
919      * @param defaultValue The default value.
920      * @return The associated string if key is found,
921      * default value otherwise.
922      * @throws ClassCastException is thrown if the key maps to an
923      * object that is not a String.
924      */

925     public String JavaDoc getString(String JavaDoc key, String JavaDoc defaultValue) {
926         Object JavaDoc value = get(key);
927
928         if (value instanceof String JavaDoc) {
929             return interpolate((String JavaDoc) value);
930             
931         } else if (value == null) {
932             if (defaults != null) {
933                 return interpolate(defaults.getString(key, defaultValue));
934             } else {
935                 return interpolate(defaultValue);
936             }
937         } else if (value instanceof Vector JavaDoc) {
938             return interpolate((String JavaDoc) ((Vector JavaDoc) value).get(0));
939         } else {
940             throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a String object");
941         }
942     }
943
944     /**
945      * Get a list of properties associated with the given
946      * configuration key.
947      *
948      * @param key The configuration key.
949      * @return The associated properties if key is found.
950      * @throws ClassCastException is thrown if the key maps to an
951      * object that is not a String/Vector.
952      * @throws IllegalArgumentException if one of the tokens is
953      * malformed (does not contain an equals sign).
954      */

955     public Properties JavaDoc getProperties(String JavaDoc key) {
956         return getProperties(key, new Properties JavaDoc());
957     }
958
959     /**
960      * Get a list of properties associated with the given
961      * configuration key.
962      *
963      * @param key The configuration key.
964      * @return The associated properties if key is found.
965      * @throws ClassCastException is thrown if the key maps to an
966      * object that is not a String/Vector.
967      * @throws IllegalArgumentException if one of the tokens is
968      * malformed (does not contain an equals sign).
969      */

970     public Properties JavaDoc getProperties(String JavaDoc key, Properties JavaDoc defaults) {
971         /*
972          * Grab an array of the tokens for this key.
973          */

974         String JavaDoc[] tokens = getStringArray(key);
975
976         // Each token is of the form 'key=value'.
977
Properties JavaDoc props = new Properties JavaDoc(defaults);
978         for (int i = 0; i < tokens.length; i++) {
979             String JavaDoc token = tokens[i];
980             int equalSign = token.indexOf('=');
981             if (equalSign > 0) {
982                 String JavaDoc pkey = token.substring(0, equalSign).trim();
983                 String JavaDoc pvalue = token.substring(equalSign + 1).trim();
984                 props.put(pkey, pvalue);
985             } else {
986                 throw new IllegalArgumentException JavaDoc('\'' + token + "' does not contain " + "an equals sign");
987             }
988         }
989         return props;
990     }
991
992     /**
993      * Get an array of strings associated with the given configuration
994      * key.
995      *
996      * @param key The configuration key.
997      * @return The associated string array if key is found.
998      * @throws ClassCastException is thrown if the key maps to an
999      * object that is not a String/Vector.
1000     */

1001    public String JavaDoc[] getStringArray(String JavaDoc key) {
1002        Object JavaDoc value = get(key);
1003
1004        // What's your vector, Victor?
1005
Vector JavaDoc vector;
1006        if (value instanceof String JavaDoc) {
1007            vector = new Vector JavaDoc(1);
1008            vector.addElement(value);
1009            
1010        } else if (value instanceof Vector JavaDoc) {
1011            vector = (Vector JavaDoc) value;
1012            
1013        } else if (value == null) {
1014            if (defaults != null) {
1015                return defaults.getStringArray(key);
1016            } else {
1017                return new String JavaDoc[0];
1018            }
1019        } else {
1020            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a String/Vector object");
1021        }
1022
1023        String JavaDoc[] tokens = new String JavaDoc[vector.size()];
1024        for (int i = 0; i < tokens.length; i++) {
1025            tokens[i] = (String JavaDoc) vector.elementAt(i);
1026        }
1027
1028        return tokens;
1029    }
1030
1031    /**
1032     * Get a Vector of strings associated with the given configuration
1033     * key.
1034     *
1035     * @param key The configuration key.
1036     * @return The associated Vector.
1037     * @throws ClassCastException is thrown if the key maps to an
1038     * object that is not a Vector.
1039     */

1040    public Vector JavaDoc getVector(String JavaDoc key) {
1041        return getVector(key, null);
1042    }
1043
1044    /**
1045     * Get a Vector of strings associated with the given configuration
1046     * key.
1047     *
1048     * @param key The configuration key.
1049     * @param defaultValue The default value.
1050     * @return The associated Vector.
1051     * @throws ClassCastException is thrown if the key maps to an
1052     * object that is not a Vector.
1053     */

1054    public Vector JavaDoc getVector(String JavaDoc key, Vector JavaDoc defaultValue) {
1055        Object JavaDoc value = get(key);
1056
1057        if (value instanceof Vector JavaDoc) {
1058            return (Vector JavaDoc) value;
1059            
1060        } else if (value instanceof String JavaDoc) {
1061            Vector JavaDoc v = new Vector JavaDoc(1);
1062            v.addElement(value);
1063            put(key, v);
1064            return v;
1065            
1066        } else if (value == null) {
1067            if (defaults != null) {
1068                return defaults.getVector(key, defaultValue);
1069            } else {
1070                return ((defaultValue == null) ? new Vector JavaDoc() : defaultValue);
1071            }
1072        } else {
1073            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Vector object");
1074        }
1075    }
1076
1077    /**
1078     * Get a boolean associated with the given configuration key.
1079     *
1080     * @param key The configuration key.
1081     * @return The associated boolean.
1082     * @throws NoSuchElementException is thrown if the key doesn't
1083     * map to an existing object.
1084     * @throws ClassCastException is thrown if the key maps to an
1085     * object that is not a Boolean.
1086     */

1087    public boolean getBoolean(String JavaDoc key) {
1088        Boolean JavaDoc b = getBoolean(key, null);
1089        if (b != null) {
1090            return b.booleanValue();
1091        } else {
1092            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1093        }
1094    }
1095
1096    /**
1097     * Get a boolean associated with the given configuration key.
1098     *
1099     * @param key The configuration key.
1100     * @param defaultValue The default value.
1101     * @return The associated boolean.
1102     * @throws ClassCastException is thrown if the key maps to an
1103     * object that is not a Boolean.
1104     */

1105    public boolean getBoolean(String JavaDoc key, boolean defaultValue) {
1106        return getBoolean(key, new Boolean JavaDoc(defaultValue)).booleanValue();
1107    }
1108
1109    /**
1110     * Get a boolean associated with the given configuration key.
1111     *
1112     * @param key The configuration key.
1113     * @param defaultValue The default value.
1114     * @return The associated boolean if key is found and has valid
1115     * format, default value otherwise.
1116     * @throws ClassCastException is thrown if the key maps to an
1117     * object that is not a Boolean.
1118     */

1119    public Boolean JavaDoc getBoolean(String JavaDoc key, Boolean JavaDoc defaultValue) {
1120
1121        Object JavaDoc value = get(key);
1122
1123        if (value instanceof Boolean JavaDoc) {
1124            return (Boolean JavaDoc) value;
1125            
1126        } else if (value instanceof String JavaDoc) {
1127            String JavaDoc s = testBoolean((String JavaDoc) value);
1128            Boolean JavaDoc b = new Boolean JavaDoc(s);
1129            put(key, b);
1130            return b;
1131            
1132        } else if (value == null) {
1133            if (defaults != null) {
1134                return defaults.getBoolean(key, defaultValue);
1135            } else {
1136                return defaultValue;
1137            }
1138        } else {
1139            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Boolean object");
1140        }
1141    }
1142
1143    /**
1144     * Test whether the string represent by value maps to a boolean
1145     * value or not. We will allow <code>true</code>, <code>on</code>,
1146     * and <code>yes</code> for a <code>true</code> boolean value, and
1147     * <code>false</code>, <code>off</code>, and <code>no</code> for
1148     * <code>false</code> boolean values. Case of value to test for
1149     * boolean status is ignored.
1150     *
1151     * @param value the value to test for boolean state
1152     * @return <code>true</code> or <code>false</code> if the supplied
1153     * text maps to a boolean value, or <code>null</code> otherwise.
1154     */

1155    public String JavaDoc testBoolean(String JavaDoc value) {
1156        String JavaDoc s = value.toLowerCase();
1157
1158        if (s.equals("true") || s.equals("on") || s.equals("yes")) {
1159            return "true";
1160        } else if (s.equals("false") || s.equals("off") || s.equals("no")) {
1161            return "false";
1162        } else {
1163            return null;
1164        }
1165    }
1166
1167    /**
1168     * Get a byte associated with the given configuration key.
1169     *
1170     * @param key The configuration key.
1171     * @return The associated byte.
1172     * @throws NoSuchElementException is thrown if the key doesn't
1173     * map to an existing object.
1174     * @throws ClassCastException is thrown if the key maps to an
1175     * object that is not a Byte.
1176     * @throws NumberFormatException is thrown if the value mapped
1177     * by the key has not a valid number format.
1178     */

1179    public byte getByte(String JavaDoc key) {
1180        Byte JavaDoc b = getByte(key, null);
1181        if (b != null) {
1182            return b.byteValue();
1183        } else {
1184            throw new NoSuchElementException JavaDoc('\'' + key + " doesn't map to an existing object");
1185        }
1186    }
1187
1188    /**
1189     * Get a byte associated with the given configuration key.
1190     *
1191     * @param key The configuration key.
1192     * @param defaultValue The default value.
1193     * @return The associated byte.
1194     * @throws ClassCastException is thrown if the key maps to an
1195     * object that is not a Byte.
1196     * @throws NumberFormatException is thrown if the value mapped
1197     * by the key has not a valid number format.
1198     */

1199    public byte getByte(String JavaDoc key, byte defaultValue) {
1200        return getByte(key, new Byte JavaDoc(defaultValue)).byteValue();
1201    }
1202
1203    /**
1204     * Get a byte associated with the given configuration key.
1205     *
1206     * @param key The configuration key.
1207     * @param defaultValue The default value.
1208     * @return The associated byte if key is found and has valid
1209     * format, default value otherwise.
1210     * @throws ClassCastException is thrown if the key maps to an
1211     * object that is not a Byte.
1212     * @throws NumberFormatException is thrown if the value mapped
1213     * by the key has not a valid number format.
1214     */

1215    public Byte JavaDoc getByte(String JavaDoc key, Byte JavaDoc defaultValue) {
1216        Object JavaDoc value = get(key);
1217
1218        if (value instanceof Byte JavaDoc) {
1219            return (Byte JavaDoc) value;
1220            
1221        } else if (value instanceof String JavaDoc) {
1222            Byte JavaDoc b = new Byte JavaDoc((String JavaDoc) value);
1223            put(key, b);
1224            return b;
1225            
1226        } else if (value == null) {
1227            if (defaults != null) {
1228                return defaults.getByte(key, defaultValue);
1229            } else {
1230                return defaultValue;
1231            }
1232        } else {
1233            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Byte object");
1234        }
1235    }
1236
1237    /**
1238     * Get a short associated with the given configuration key.
1239     *
1240     * @param key The configuration key.
1241     * @return The associated short.
1242     * @throws NoSuchElementException is thrown if the key doesn't
1243     * map to an existing object.
1244     * @throws ClassCastException is thrown if the key maps to an
1245     * object that is not a Short.
1246     * @throws NumberFormatException is thrown if the value mapped
1247     * by the key has not a valid number format.
1248     */

1249    public short getShort(String JavaDoc key) {
1250        Short JavaDoc s = getShort(key, null);
1251        if (s != null) {
1252            return s.shortValue();
1253        } else {
1254            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1255        }
1256    }
1257
1258    /**
1259     * Get a short associated with the given configuration key.
1260     *
1261     * @param key The configuration key.
1262     * @param defaultValue The default value.
1263     * @return The associated short.
1264     * @throws ClassCastException is thrown if the key maps to an
1265     * object that is not a Short.
1266     * @throws NumberFormatException is thrown if the value mapped
1267     * by the key has not a valid number format.
1268     */

1269    public short getShort(String JavaDoc key, short defaultValue) {
1270        return getShort(key, new Short JavaDoc(defaultValue)).shortValue();
1271    }
1272
1273    /**
1274     * Get a short associated with the given configuration key.
1275     *
1276     * @param key The configuration key.
1277     * @param defaultValue The default value.
1278     * @return The associated short if key is found and has valid
1279     * format, default value otherwise.
1280     * @throws ClassCastException is thrown if the key maps to an
1281     * object that is not a Short.
1282     * @throws NumberFormatException is thrown if the value mapped
1283     * by the key has not a valid number format.
1284     */

1285    public Short JavaDoc getShort(String JavaDoc key, Short JavaDoc defaultValue) {
1286        Object JavaDoc value = get(key);
1287
1288        if (value instanceof Short JavaDoc) {
1289            return (Short JavaDoc) value;
1290            
1291        } else if (value instanceof String JavaDoc) {
1292            Short JavaDoc s = new Short JavaDoc((String JavaDoc) value);
1293            put(key, s);
1294            return s;
1295            
1296        } else if (value == null) {
1297            if (defaults != null) {
1298                return defaults.getShort(key, defaultValue);
1299            } else {
1300                return defaultValue;
1301            }
1302        } else {
1303            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Short object");
1304        }
1305    }
1306
1307    /**
1308     * The purpose of this method is to get the configuration resource
1309     * with the given name as an integer.
1310     *
1311     * @param name The resource name.
1312     * @return The value of the resource as an integer.
1313     */

1314    public int getInt(String JavaDoc name) {
1315        return getInteger(name);
1316    }
1317
1318    /**
1319     * The purpose of this method is to get the configuration resource
1320     * with the given name as an integer, or a default value.
1321     *
1322     * @param name The resource name
1323     * @param def The default value of the resource.
1324     * @return The value of the resource as an integer.
1325     */

1326    public int getInt(String JavaDoc name, int def) {
1327        return getInteger(name, def);
1328    }
1329
1330    /**
1331     * Get a int associated with the given configuration key.
1332     *
1333     * @param key The configuration key.
1334     * @return The associated int.
1335     * @throws NoSuchElementException is thrown if the key doesn't
1336     * map to an existing object.
1337     * @throws ClassCastException is thrown if the key maps to an
1338     * object that is not a Integer.
1339     * @throws NumberFormatException is thrown if the value mapped
1340     * by the key has not a valid number format.
1341     */

1342    public int getInteger(String JavaDoc key) {
1343        Integer JavaDoc i = getInteger(key, null);
1344        if (i != null) {
1345            return i.intValue();
1346        } else {
1347            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1348        }
1349    }
1350
1351    /**
1352     * Get a int associated with the given configuration key.
1353     *
1354     * @param key The configuration key.
1355     * @param defaultValue The default value.
1356     * @return The associated int.
1357     * @throws ClassCastException is thrown if the key maps to an
1358     * object that is not a Integer.
1359     * @throws NumberFormatException is thrown if the value mapped
1360     * by the key has not a valid number format.
1361     */

1362    public int getInteger(String JavaDoc key, int defaultValue) {
1363        Integer JavaDoc i = getInteger(key, null);
1364
1365        if (i == null) {
1366            return defaultValue;
1367        }
1368        return i.intValue();
1369    }
1370
1371    /**
1372     * Get a int associated with the given configuration key.
1373     *
1374     * @param key The configuration key.
1375     * @param defaultValue The default value.
1376     * @return The associated int if key is found and has valid
1377     * format, default value otherwise.
1378     * @throws ClassCastException is thrown if the key maps to an
1379     * object that is not a Integer.
1380     * @throws NumberFormatException is thrown if the value mapped
1381     * by the key has not a valid number format.
1382     */

1383    public Integer JavaDoc getInteger(String JavaDoc key, Integer JavaDoc defaultValue) {
1384        Object JavaDoc value = get(key);
1385
1386        if (value instanceof Integer JavaDoc) {
1387            return (Integer JavaDoc) value;
1388            
1389        } else if (value instanceof String JavaDoc) {
1390            Integer JavaDoc i = new Integer JavaDoc((String JavaDoc) value);
1391            put(key, i);
1392            return i;
1393            
1394        } else if (value == null) {
1395            if (defaults != null) {
1396                return defaults.getInteger(key, defaultValue);
1397            } else {
1398                return defaultValue;
1399            }
1400        } else {
1401            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Integer object");
1402        }
1403    }
1404
1405    /**
1406     * Get a long associated with the given configuration key.
1407     *
1408     * @param key The configuration key.
1409     * @return The associated long.
1410     * @throws NoSuchElementException is thrown if the key doesn't
1411     * map to an existing object.
1412     * @throws ClassCastException is thrown if the key maps to an
1413     * object that is not a Long.
1414     * @throws NumberFormatException is thrown if the value mapped
1415     * by the key has not a valid number format.
1416     */

1417    public long getLong(String JavaDoc key) {
1418        Long JavaDoc l = getLong(key, null);
1419        if (l != null) {
1420            return l.longValue();
1421        } else {
1422            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1423        }
1424    }
1425
1426    /**
1427     * Get a long associated with the given configuration key.
1428     *
1429     * @param key The configuration key.
1430     * @param defaultValue The default value.
1431     * @return The associated long.
1432     * @throws ClassCastException is thrown if the key maps to an
1433     * object that is not a Long.
1434     * @throws NumberFormatException is thrown if the value mapped
1435     * by the key has not a valid number format.
1436     */

1437    public long getLong(String JavaDoc key, long defaultValue) {
1438        return getLong(key, new Long JavaDoc(defaultValue)).longValue();
1439    }
1440
1441    /**
1442     * Get a long associated with the given configuration key.
1443     *
1444     * @param key The configuration key.
1445     * @param defaultValue The default value.
1446     * @return The associated long if key is found and has valid
1447     * format, default value otherwise.
1448     * @throws ClassCastException is thrown if the key maps to an
1449     * object that is not a Long.
1450     * @throws NumberFormatException is thrown if the value mapped
1451     * by the key has not a valid number format.
1452     */

1453    public Long JavaDoc getLong(String JavaDoc key, Long JavaDoc defaultValue) {
1454        Object JavaDoc value = get(key);
1455
1456        if (value instanceof Long JavaDoc) {
1457            return (Long JavaDoc) value;
1458            
1459        } else if (value instanceof String JavaDoc) {
1460            Long JavaDoc l = new Long JavaDoc((String JavaDoc) value);
1461            put(key, l);
1462            return l;
1463            
1464        } else if (value == null) {
1465            if (defaults != null) {
1466                return defaults.getLong(key, defaultValue);
1467            } else {
1468                return defaultValue;
1469            }
1470        } else {
1471            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Long object");
1472        }
1473    }
1474
1475    /**
1476     * Get a float associated with the given configuration key.
1477     *
1478     * @param key The configuration key.
1479     * @return The associated float.
1480     * @throws NoSuchElementException is thrown if the key doesn't
1481     * map to an existing object.
1482     * @throws ClassCastException is thrown if the key maps to an
1483     * object that is not a Float.
1484     * @throws NumberFormatException is thrown if the value mapped
1485     * by the key has not a valid number format.
1486     */

1487    public float getFloat(String JavaDoc key) {
1488        Float JavaDoc f = getFloat(key, null);
1489        if (f != null) {
1490            return f.floatValue();
1491        } else {
1492            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1493        }
1494    }
1495
1496    /**
1497     * Get a float associated with the given configuration key.
1498     *
1499     * @param key The configuration key.
1500     * @param defaultValue The default value.
1501     * @return The associated float.
1502     * @throws ClassCastException is thrown if the key maps to an
1503     * object that is not a Float.
1504     * @throws NumberFormatException is thrown if the value mapped
1505     * by the key has not a valid number format.
1506     */

1507    public float getFloat(String JavaDoc key, float defaultValue) {
1508        return getFloat(key, new Float JavaDoc(defaultValue)).floatValue();
1509    }
1510
1511    /**
1512     * Get a float associated with the given configuration key.
1513     *
1514     * @param key The configuration key.
1515     * @param defaultValue The default value.
1516     * @return The associated float if key is found and has valid
1517     * format, default value otherwise.
1518     * @throws ClassCastException is thrown if the key maps to an
1519     * object that is not a Float.
1520     * @throws NumberFormatException is thrown if the value mapped
1521     * by the key has not a valid number format.
1522     */

1523    public Float JavaDoc getFloat(String JavaDoc key, Float JavaDoc defaultValue) {
1524        Object JavaDoc value = get(key);
1525
1526        if (value instanceof Float JavaDoc) {
1527            return (Float JavaDoc) value;
1528            
1529        } else if (value instanceof String JavaDoc) {
1530            Float JavaDoc f = new Float JavaDoc((String JavaDoc) value);
1531            put(key, f);
1532            return f;
1533            
1534        } else if (value == null) {
1535            if (defaults != null) {
1536                return defaults.getFloat(key, defaultValue);
1537            } else {
1538                return defaultValue;
1539            }
1540        } else {
1541            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Float object");
1542        }
1543    }
1544
1545    /**
1546     * Get a double associated with the given configuration key.
1547     *
1548     * @param key The configuration key.
1549     * @return The associated double.
1550     * @throws NoSuchElementException is thrown if the key doesn't
1551     * map to an existing object.
1552     * @throws ClassCastException is thrown if the key maps to an
1553     * object that is not a Double.
1554     * @throws NumberFormatException is thrown if the value mapped
1555     * by the key has not a valid number format.
1556     */

1557    public double getDouble(String JavaDoc key) {
1558        Double JavaDoc d = getDouble(key, null);
1559        if (d != null) {
1560            return d.doubleValue();
1561        } else {
1562            throw new NoSuchElementException JavaDoc('\'' + key + "' doesn't map to an existing object");
1563        }
1564    }
1565
1566    /**
1567     * Get a double associated with the given configuration key.
1568     *
1569     * @param key The configuration key.
1570     * @param defaultValue The default value.
1571     * @return The associated double.
1572     * @throws ClassCastException is thrown if the key maps to an
1573     * object that is not a Double.
1574     * @throws NumberFormatException is thrown if the value mapped
1575     * by the key has not a valid number format.
1576     */

1577    public double getDouble(String JavaDoc key, double defaultValue) {
1578        return getDouble(key, new Double JavaDoc(defaultValue)).doubleValue();
1579    }
1580
1581    /**
1582     * Get a double associated with the given configuration key.
1583     *
1584     * @param key The configuration key.
1585     * @param defaultValue The default value.
1586     * @return The associated double if key is found and has valid
1587     * format, default value otherwise.
1588     * @throws ClassCastException is thrown if the key maps to an
1589     * object that is not a Double.
1590     * @throws NumberFormatException is thrown if the value mapped
1591     * by the key has not a valid number format.
1592     */

1593    public Double JavaDoc getDouble(String JavaDoc key, Double JavaDoc defaultValue) {
1594        Object JavaDoc value = get(key);
1595
1596        if (value instanceof Double JavaDoc) {
1597            return (Double JavaDoc) value;
1598            
1599        } else if (value instanceof String JavaDoc) {
1600            Double JavaDoc d = new Double JavaDoc((String JavaDoc) value);
1601            put(key, d);
1602            return d;
1603            
1604        } else if (value == null) {
1605            if (defaults != null) {
1606                return defaults.getDouble(key, defaultValue);
1607            } else {
1608                return defaultValue;
1609            }
1610        } else {
1611            throw new ClassCastException JavaDoc('\'' + key + "' doesn't map to a Double object");
1612        }
1613    }
1614
1615    /**
1616     * Convert a standard properties class into a configuration class.
1617     *
1618     * @param props the properties object to convert
1619     * @return new ExtendedProperties created from props
1620     */

1621    public static ExtendedProperties convertProperties(Properties JavaDoc props) {
1622        ExtendedProperties c = new ExtendedProperties();
1623
1624        for (Enumeration JavaDoc e = props.keys(); e.hasMoreElements();) {
1625            String JavaDoc s = (String JavaDoc) e.nextElement();
1626            c.setProperty(s, props.getProperty(s));
1627        }
1628
1629        return c;
1630    }
1631    
1632}
1633
Popular Tags