1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 import java.util.Vector;
29
30 import org.apache.commons.collections.Predicate;
31 import org.apache.commons.collections.iterators.FilterIterator;
32 import org.apache.commons.lang.BooleanUtils;
33
34 /***
35 * Abstract configuration class. Provide basic functionality but does not
36 * store any data. If you want to write your own Configuration class
37 * then you should implement only abstract methods from this class.
38 *
39 * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov</a>
40 * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
41 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
42 * @version $Id: AbstractConfiguration.java,v 1.25 2004/10/05 21:17:25 ebourg Exp $
43 */
44 public abstract class AbstractConfiguration implements Configuration
45 {
46 /*** start token */
47 protected static final String START_TOKEN = "${";
48
49 /*** end token */
50 protected static final String END_TOKEN = "}";
51
52 /*** The property delimiter used while parsing (a comma). */
53 private static char DELIMITER = ',';
54
55 /*** how big the initial arraylist for splitting up name value pairs */
56 private static final int INITIAL_LIST_SIZE = 2;
57
58 /***
59 * Whether the configuration should throw NoSuchElementExceptions or simply
60 * return null when a property does not exist. Defaults to return null.
61 */
62 private boolean throwExceptionOnMissing = false;
63
64 /***
65 * For configurations extending AbstractConfiguration, allow them to
66 * change the delimiter from the default comma (",").
67 *
68 * @param delimiter The new delimiter
69 */
70 public static void setDelimiter(char delimiter)
71 {
72 AbstractConfiguration.DELIMITER = delimiter;
73 }
74
75 /***
76 * Retrieve the current delimiter. By default this is a comma (",").
77 *
78 * @return The delimiter in use
79 */
80 public static char getDelimiter()
81 {
82 return AbstractConfiguration.DELIMITER;
83 }
84
85 /***
86 * If set to false, missing elements return null if possible (for objects).
87 *
88 * @param throwExceptionOnMissing The new value for the property
89 */
90 public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
91 {
92 this.throwExceptionOnMissing = throwExceptionOnMissing;
93 }
94
95 /***
96 * Returns true if missing values throw Exceptions.
97 *
98 * @return true if missing values throw Exceptions
99 */
100 public boolean isThrowExceptionOnMissing()
101 {
102 return throwExceptionOnMissing;
103 }
104
105
106 /***
107 * {@inheritDoc}
108 */
109 public void addProperty(String key, Object token)
110 {
111 if (token instanceof String)
112 {
113 Iterator it = split((String) token).iterator();
114 while (it.hasNext())
115 {
116 addPropertyDirect(key, it.next());
117 }
118 }
119 else if (token instanceof Collection)
120 {
121 Iterator it = ((Collection) token).iterator();
122 while (it.hasNext())
123 {
124 addProperty(key, it.next());
125 }
126 }
127 else
128 {
129 addPropertyDirect(key, token);
130 }
131 }
132
133 /***
134 * Read property. Should return <code>null</code> if the key doesn't
135 * map to an existing object.
136 *
137 * @param key key to use for mapping
138 *
139 * @return object associated with the given configuration key.
140 */
141 protected abstract Object getPropertyDirect(String key);
142
143 /***
144 * Adds a key/value pair to the Configuration. Override this method to
145 * provide write acces to underlying Configuration store.
146 *
147 * @param key key to use for mapping
148 * @param obj object to store
149 */
150 protected abstract void addPropertyDirect(String key, Object obj);
151
152 /***
153 * interpolate key names to handle ${key} stuff
154 *
155 * @param base string to interpolate
156 *
157 * @return returns the key name with the ${key} substituted
158 */
159 protected String interpolate(String base)
160 {
161 return interpolateHelper(base, null);
162 }
163
164 /***
165 * Recursive handler for multple levels of interpolation.
166 *
167 * When called the first time, priorVariables should be null.
168 *
169 * @param base string with the ${key} variables
170 * @param priorVariables serves two purposes: to allow checking for
171 * loops, and creating a meaningful exception message should a loop
172 * occur. It's 0'th element will be set to the value of base from
173 * the first call. All subsequent interpolated variables are added
174 * afterward.
175 *
176 * @return the string with the interpolation taken care of
177 */
178 protected String interpolateHelper(String base, List priorVariables)
179 {
180 if (base == null)
181 {
182 return null;
183 }
184
185
186
187 if (priorVariables == null)
188 {
189 priorVariables = new ArrayList();
190 priorVariables.add(base);
191 }
192
193 int begin = -1;
194 int end = -1;
195 int prec = 0 - END_TOKEN.length();
196 String variable = null;
197 StringBuffer result = new StringBuffer();
198
199
200 while (((begin = base.indexOf(START_TOKEN, prec + END_TOKEN.length()))
201 > -1)
202 && ((end = base.indexOf(END_TOKEN, begin)) > -1))
203 {
204 result.append(base.substring(prec + END_TOKEN.length(), begin));
205 variable = base.substring(begin + START_TOKEN.length(), end);
206
207
208 if (priorVariables.contains(variable))
209 {
210 String initialBase = priorVariables.remove(0).toString();
211 priorVariables.add(variable);
212 StringBuffer priorVariableSb = new StringBuffer();
213
214
215
216 for (Iterator it = priorVariables.iterator(); it.hasNext();)
217 {
218 priorVariableSb.append(it.next());
219 if (it.hasNext())
220 {
221 priorVariableSb.append("->");
222 }
223 }
224
225 throw new IllegalStateException(
226 "infinite loop in property interpolation of "
227 + initialBase
228 + ": "
229 + priorVariableSb.toString());
230 }
231
232 else
233 {
234 priorVariables.add(variable);
235 }
236
237
238 Object value = getProperty(variable);
239 if (value != null)
240 {
241 result.append(interpolateHelper(value.toString(),
242 priorVariables));
243
244
245
246
247
248 priorVariables.remove(priorVariables.size() - 1);
249 }
250 else
251 {
252
253 result.append(START_TOKEN).append(variable).append(END_TOKEN);
254 }
255
256 prec = end;
257 }
258 result.append(base.substring(prec + END_TOKEN.length(), base.length()));
259 return result.toString();
260 }
261
262 /***
263 * Returns a List of Strings built from the supplied String. Splits up CSV
264 * lists. If no commas are in the String, simply returns a List with the
265 * String as its first element.
266 *
267 * @param token The String to tokenize
268 *
269 * @return A List of Strings
270 */
271 protected List split(String token)
272 {
273 List list = new ArrayList(INITIAL_LIST_SIZE);
274
275 if (token.indexOf(DELIMITER) > 0)
276 {
277 PropertiesTokenizer tokenizer = new PropertiesTokenizer(token);
278
279 while (tokenizer.hasMoreTokens())
280 {
281 list.add(tokenizer.nextToken());
282 }
283 }
284 else
285 {
286 list.add(token);
287 }
288
289
290
291
292
293
294 return list;
295 }
296
297 /***
298 * {@inheritDoc}
299 */
300 public Configuration subset(String prefix)
301 {
302 return new SubsetConfiguration(this, prefix, ".");
303 }
304
305 /***
306 * {@inheritDoc}
307 */
308 public abstract boolean isEmpty();
309
310 /***
311 * {@inheritDoc}
312 */
313 public abstract boolean containsKey(String key);
314
315 /***
316 * {@inheritDoc}
317 */
318 public void setProperty(String key, Object value)
319 {
320 clearProperty(key);
321 addProperty(key, value);
322 }
323
324 /***
325 * {@inheritDoc}
326 */
327 public abstract void clearProperty(String key);
328
329 /***
330 * {@inheritDoc}
331 */
332 public abstract Iterator getKeys();
333
334 /***
335 * {@inheritDoc}
336 */
337 public Iterator getKeys(final String prefix)
338 {
339 return new FilterIterator(getKeys(), new Predicate()
340 {
341 public boolean evaluate(Object obj)
342 {
343 String key = (String) obj;
344 return key.startsWith(prefix + ".") || key.equals(prefix);
345 }
346 });
347 }
348
349 /***
350 * {@inheritDoc}
351 */
352 public Properties getProperties(String key)
353 {
354 return getProperties(key, null);
355 }
356
357 /***
358 * Get a list of properties associated with the given configuration key.
359 *
360 * @param key The configuration key.
361 * @param defaults Any default values for the returned
362 * <code>Properties</code> object. Ignored if <code>null</code>.
363 *
364 * @return The associated properties if key is found.
365 *
366 * @throws ConversionException is thrown if the key maps to an
367 * object that is not a String/List of Strings.
368 *
369 * @throws IllegalArgumentException if one of the tokens is
370 * malformed (does not contain an equals sign).
371 */
372 public Properties getProperties(String key, Properties defaults)
373 {
374
375
376
377 String[] tokens = getStringArray(key);
378
379
380
381
382 Properties props = defaults == null ? new Properties() : new Properties(defaults);
383 for (int i = 0; i < tokens.length; i++)
384 {
385 String token = tokens[i];
386 int equalSign = token.indexOf('=');
387 if (equalSign > 0)
388 {
389 String pkey = token.substring(0, equalSign).trim();
390 String pvalue = token.substring(equalSign + 1).trim();
391 props.put(pkey, pvalue);
392 }
393 else if (tokens.length == 1 && "".equals(token))
394 {
395
396
397 break;
398 }
399 else
400 {
401 throw new IllegalArgumentException(
402 '\'' + token + "' does not contain an equals sign");
403 }
404 }
405 return props;
406 }
407
408 /***
409 * {@inheritDoc}
410 */
411 public Object getProperty(String key)
412 {
413 return getPropertyDirect(key);
414 }
415
416 /***
417 * {@inheritDoc}
418 */
419 public boolean getBoolean(String key)
420 {
421 Boolean b = getBoolean(key, null);
422 if (b != null)
423 {
424 return b.booleanValue();
425 }
426 else
427 {
428 throw new NoSuchElementException(
429 '\'' + key + "' doesn't map to an existing object");
430 }
431 }
432
433 /***
434 * {@inheritDoc}
435 */
436 public boolean getBoolean(String key, boolean defaultValue)
437 {
438 return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue();
439 }
440
441 /***
442 * {@inheritDoc}
443 */
444 public Boolean getBoolean(String key, Boolean defaultValue)
445 {
446 Object value = resolveContainerStore(key);
447
448 if (value instanceof Boolean)
449 {
450 return (Boolean) value;
451 }
452 else if (value instanceof String)
453 {
454 Boolean b = BooleanUtils.toBooleanObject((String) value);
455 if (b == null)
456 {
457 throw new ConversionException('\'' + key + "' doesn't map to a Boolean object");
458 }
459 return b;
460 }
461 else if (value == null)
462 {
463 return defaultValue;
464 }
465 else
466 {
467 throw new ConversionException(
468 '\'' + key + "' doesn't map to a Boolean object");
469 }
470 }
471
472 /***
473 * {@inheritDoc}
474 */
475 public byte getByte(String key)
476 {
477 Byte b = getByte(key, null);
478 if (b != null)
479 {
480 return b.byteValue();
481 }
482 else
483 {
484 throw new NoSuchElementException(
485 '\'' + key + " doesn't map to an existing object");
486 }
487 }
488
489 /***
490 * {@inheritDoc}
491 */
492 public byte getByte(String key, byte defaultValue)
493 {
494 return getByte(key, new Byte(defaultValue)).byteValue();
495 }
496
497 /***
498 * {@inheritDoc}
499 */
500 public Byte getByte(String key, Byte defaultValue)
501 {
502 Object value = resolveContainerStore(key);
503
504 if (value instanceof Byte)
505 {
506 return (Byte) value;
507 }
508 else if (value instanceof String)
509 {
510 try
511 {
512 Byte b = new Byte((String) value);
513 return b;
514 }
515 catch (NumberFormatException e)
516 {
517 throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e);
518 }
519 }
520 else if (value == null)
521 {
522 return defaultValue;
523 }
524 else
525 {
526 throw new ConversionException('\'' + key + "' doesn't map to a Byte object");
527 }
528 }
529
530 /***
531 * {@inheritDoc}
532 */
533 public double getDouble(String key)
534 {
535 Double d = getDouble(key, null);
536 if (d != null)
537 {
538 return d.doubleValue();
539 }
540 else
541 {
542 throw new NoSuchElementException(
543 '\'' + key + "' doesn't map to an existing object");
544 }
545 }
546
547 /***
548 * {@inheritDoc}
549 */
550 public double getDouble(String key, double defaultValue)
551 {
552 return getDouble(key, new Double(defaultValue)).doubleValue();
553 }
554
555 /***
556 * {@inheritDoc}
557 */
558 public Double getDouble(String key, Double defaultValue)
559 {
560 Object value = resolveContainerStore(key);
561
562 if (value instanceof Double)
563 {
564 return (Double) value;
565 }
566 else if (value instanceof String)
567 {
568 try
569 {
570 Double d = new Double((String) value);
571 return d;
572 }
573 catch (NumberFormatException e)
574 {
575 throw new ConversionException('\'' + key + "' doesn't map to a Double object", e);
576 }
577 }
578 else if (value == null)
579 {
580 return defaultValue;
581 }
582 else
583 {
584 throw new ConversionException('\'' + key + "' doesn't map to a Double object");
585 }
586 }
587
588 /***
589 * {@inheritDoc}
590 */
591 public float getFloat(String key)
592 {
593 Float f = getFloat(key, null);
594 if (f != null)
595 {
596 return f.floatValue();
597 }
598 else
599 {
600 throw new NoSuchElementException(
601 '\'' + key + "' doesn't map to an existing object");
602 }
603 }
604
605 /***
606 * {@inheritDoc}
607 */
608 public float getFloat(String key, float defaultValue)
609 {
610 return getFloat(key, new Float(defaultValue)).floatValue();
611 }
612
613 /***
614 * {@inheritDoc}
615 */
616 public Float getFloat(String key, Float defaultValue)
617 {
618 Object value = resolveContainerStore(key);
619
620 if (value instanceof Float)
621 {
622 return (Float) value;
623 }
624 else if (value instanceof String)
625 {
626 try
627 {
628 Float f = new Float((String) value);
629 return f;
630 }
631 catch (NumberFormatException e)
632 {
633 throw new ConversionException('\'' + key + "' doesn't map to a Float object", e);
634 }
635 }
636 else if (value == null)
637 {
638 return defaultValue;
639 }
640 else
641 {
642 throw new ConversionException('\'' + key + "' doesn't map to a Float object");
643 }
644 }
645
646 /***
647 * {@inheritDoc}
648 */
649 public int getInt(String key)
650 {
651 Integer i = getInteger(key, null);
652 if (i != null)
653 {
654 return i.intValue();
655 }
656 else
657 {
658 throw new NoSuchElementException(
659 '\'' + key + "' doesn't map to an existing object");
660 }
661 }
662
663 /***
664 * {@inheritDoc}
665 */
666 public int getInt(String key, int defaultValue)
667 {
668 Integer i = getInteger(key, null);
669
670 if (i == null)
671 {
672 return defaultValue;
673 }
674
675 return i.intValue();
676 }
677
678 /***
679 * {@inheritDoc}
680 */
681 public Integer getInteger(String key, Integer defaultValue)
682 {
683 Object value = resolveContainerStore(key);
684
685 if (value instanceof Integer)
686 {
687 return (Integer) value;
688 }
689 else if (value instanceof String)
690 {
691 try
692 {
693 Integer i = new Integer((String) value);
694 return i;
695 }
696 catch (NumberFormatException e)
697 {
698 throw new ConversionException('\'' + key + "' doesn't map to a Integer object", e);
699 }
700 }
701 else if (value == null)
702 {
703 return defaultValue;
704 }
705 else
706 {
707 throw new ConversionException('\'' + key + "' doesn't map to a Integer object");
708 }
709 }
710
711 /***
712 * {@inheritDoc}
713 */
714 public long getLong(String key)
715 {
716 Long l = getLong(key, null);
717 if (l != null)
718 {
719 return l.longValue();
720 }
721 else
722 {
723 throw new NoSuchElementException(
724 '\'' + key + "' doesn't map to an existing object");
725 }
726 }
727
728 /***
729 * {@inheritDoc}
730 */
731 public long getLong(String key, long defaultValue)
732 {
733 return getLong(key, new Long(defaultValue)).longValue();
734 }
735
736 /***
737 * {@inheritDoc}
738 */
739 public Long getLong(String key, Long defaultValue)
740 {
741 Object value = resolveContainerStore(key);
742
743 if (value instanceof Long)
744 {
745 return (Long) value;
746 }
747 else if (value instanceof String)
748 {
749 try
750 {
751 Long l = new Long((String) value);
752 return l;
753 }
754 catch (NumberFormatException e)
755 {
756 throw new ConversionException('\'' + key + "' doesn't map to a Long object", e);
757 }
758 }
759 else if (value == null)
760 {
761 return defaultValue;
762 }
763 else
764 {
765 throw new ConversionException('\'' + key + "' doesn't map to a Long object");
766 }
767 }
768
769 /***
770 * {@inheritDoc}
771 */
772 public short getShort(String key)
773 {
774 Short s = getShort(key, null);
775 if (s != null)
776 {
777 return s.shortValue();
778 }
779 else
780 {
781 throw new NoSuchElementException(
782 '\'' + key + "' doesn't map to an existing object");
783 }
784 }
785
786 /***
787 * {@inheritDoc}
788 */
789 public short getShort(String key, short defaultValue)
790 {
791 return getShort(key, new Short(defaultValue)).shortValue();
792 }
793
794 /***
795 * {@inheritDoc}
796 */
797 public Short getShort(String key, Short defaultValue)
798 {
799 Object value = resolveContainerStore(key);
800
801 if (value instanceof Short)
802 {
803 return (Short) value;
804 }
805 else if (value instanceof String)
806 {
807 try
808 {
809 Short s = new Short((String) value);
810 return s;
811 }
812 catch (NumberFormatException e)
813 {
814 throw new ConversionException('\'' + key + "' doesn't map to a Short object", e);
815 }
816 }
817 else if (value == null)
818 {
819 return defaultValue;
820 }
821 else
822 {
823 throw new ConversionException('\'' + key + "' doesn't map to a Short object");
824 }
825 }
826
827 /***
828 * {@inheritDoc}
829 */
830 public BigDecimal getBigDecimal(String key)
831 {
832 BigDecimal number = getBigDecimal(key, null);
833 if (number != null)
834 {
835 return number;
836 }
837 else if (isThrowExceptionOnMissing())
838 {
839 throw new NoSuchElementException(
840 '\'' + key + "' doesn't map to an existing object");
841 }
842 else
843 {
844 return null;
845 }
846 }
847
848 /***
849 * {@inheritDoc}
850 */
851 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
852 {
853 Object value = resolveContainerStore(key);
854
855 if (value instanceof BigDecimal)
856 {
857 return (BigDecimal) value;
858 }
859 else if (value instanceof String)
860 {
861 try
862 {
863 BigDecimal number = new BigDecimal((String) value);
864 return number;
865 }
866 catch (Exception e)
867 {
868 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
869 }
870 }
871 else if (value == null)
872 {
873 return defaultValue;
874 }
875 else
876 {
877 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object");
878 }
879 }
880
881 /***
882 * {@inheritDoc}
883 */
884 public BigInteger getBigInteger(String key)
885 {
886 BigInteger number = getBigInteger(key, null);
887 if (number != null)
888 {
889 return number;
890 }
891 else if (isThrowExceptionOnMissing())
892 {
893 throw new NoSuchElementException(
894 '\'' + key + "' doesn't map to an existing object");
895 }
896 else
897 {
898 return null;
899 }
900 }
901
902 /***
903 * {@inheritDoc}
904 */
905 public BigInteger getBigInteger(String key, BigInteger defaultValue)
906 {
907 Object value = resolveContainerStore(key);
908
909 if (value instanceof BigInteger)
910 {
911 return (BigInteger) value;
912 }
913 else if (value instanceof String)
914 {
915 try
916 {
917 BigInteger number = new BigInteger((String) value);
918 return number;
919 }
920 catch (Exception e)
921 {
922 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
923 }
924 }
925 else if (value == null)
926 {
927 return defaultValue;
928 }
929 else
930 {
931 throw new ConversionException(
932 '\'' + key + "' doesn't map to a BigDecimal object");
933 }
934 }
935
936 /***
937 * {@inheritDoc}
938 */
939 public String getString(String key)
940 {
941 String s = getString(key, null);
942 if (s != null)
943 {
944 return s;
945 }
946 else if (isThrowExceptionOnMissing())
947 {
948 throw new NoSuchElementException(
949 '\'' + key + "' doesn't map to an existing object");
950 }
951 else
952 {
953 return null;
954 }
955 }
956
957 /***
958 * {@inheritDoc}
959 */
960 public String getString(String key, String defaultValue)
961 {
962 Object value = resolveContainerStore(key);
963
964 if (value instanceof String)
965 {
966 return interpolate((String) value);
967 }
968 else if (value == null)
969 {
970 return interpolate(defaultValue);
971 }
972 else
973 {
974 throw new ConversionException(
975 '\'' + key + "' doesn't map to a String object");
976 }
977 }
978
979 /***
980 * {@inheritDoc}
981 */
982 public String[] getStringArray(String key)
983 {
984 Object value = getPropertyDirect(key);
985
986 String[] tokens;
987
988 if (value instanceof String)
989 {
990 tokens = new String[1];
991
992 tokens[0] = interpolate((String) value);
993 }
994 else if (value instanceof List)
995 {
996 List list = (List) value;
997 tokens = new String[list.size()];
998
999 for (int i = 0; i < tokens.length; i++)
1000 {
1001 tokens[i] = interpolate((String) list.get(i));
1002 }
1003 }
1004 else if (value == null)
1005 {
1006 tokens = new String[0];
1007 }
1008 else
1009 {
1010 throw new ConversionException(
1011 '\'' + key + "' doesn't map to a String/List object");
1012 }
1013 return tokens;
1014 }
1015
1016 /***
1017 * {@inheritDoc}
1018 */
1019 public List getList(String key)
1020 {
1021 return getList(key, new ArrayList());
1022 }
1023
1024 /***
1025 * {@inheritDoc}
1026 */
1027 public List getList(String key, List defaultValue)
1028 {
1029 Object value = getPropertyDirect(key);
1030 List list = null;
1031
1032 if (value instanceof String)
1033 {
1034 list = new ArrayList(1);
1035 list.add(value);
1036 }
1037 else if (value instanceof List)
1038 {
1039 list = (List) value;
1040 }
1041 else if (value == null)
1042 {
1043 list = defaultValue;
1044 }
1045 else
1046 {
1047 throw new ConversionException(
1048 '\''
1049 + key
1050 + "' doesn't map to a List object: "
1051 + value
1052 + ", a "
1053 + value.getClass().getName());
1054 }
1055 return list;
1056 }
1057
1058 /***
1059 * {@inheritDoc}
1060 */
1061 public Vector getVector(String key)
1062 {
1063 return getVector(key, new Vector());
1064 }
1065
1066 /***
1067 * {@inheritDoc}
1068 */
1069 public Vector getVector(String key, Vector defaultValue)
1070 {
1071 Object value = getPropertyDirect(key);
1072 Vector vector = null;
1073
1074 if (value instanceof String)
1075 {
1076 vector = new Vector(1);
1077 vector.add(value);
1078 }
1079 else if (value instanceof List)
1080 {
1081 vector = new Vector(((List) value).size());
1082
1083 for (Iterator it = ((List) value).iterator(); it.hasNext(); )
1084 {
1085 Object obj = it.next();
1086 vector.add(obj);
1087 }
1088 }
1089 else if (value == null)
1090 {
1091 vector = defaultValue;
1092 }
1093 else
1094 {
1095 throw new ConversionException(
1096 '\''
1097 + key
1098 + "' doesn't map to a Vector object: "
1099 + value
1100 + ", a "
1101 + value.getClass().getName());
1102 }
1103 return vector;
1104 }
1105
1106 /***
1107 * Returns an object from the store described by the key. If the value is
1108 * a List object, replace it with the first object in the list.
1109 *
1110 * @param key The property key.
1111 *
1112 * @return value Value, transparently resolving a possible List dependency.
1113 */
1114 private Object resolveContainerStore(String key)
1115 {
1116 Object value = getPropertyDirect(key);
1117 if (value != null && value instanceof List)
1118 {
1119 List list = (List) value;
1120 value = list.isEmpty() ? null : list.get(0);
1121 }
1122 return value;
1123 }
1124
1125 /***
1126 * This class divides into tokens a property value. Token
1127 * separator is "," but commas into the property value are escaped
1128 * using the backslash in front.
1129 */
1130 static class PropertiesTokenizer extends StringTokenizer
1131 {
1132 /***
1133 * Constructor.
1134 *
1135 * @param string A String.
1136 */
1137 public PropertiesTokenizer(String string)
1138 {
1139 super(string, String.valueOf(DELIMITER));
1140 }
1141
1142 /***
1143 * Get next token.
1144 *
1145 * @return A String.
1146 */
1147 public String nextToken()
1148 {
1149 StringBuffer buffer = new StringBuffer();
1150
1151 while (hasMoreTokens())
1152 {
1153 String token = super.nextToken();
1154 if (token.endsWith("//"))
1155 {
1156 buffer.append(token.substring(0, token.length() - 1));
1157 buffer.append(DELIMITER);
1158 }
1159 else
1160 {
1161 buffer.append(token);
1162 break;
1163 }
1164 }
1165 return buffer.toString().trim();
1166 }
1167 }
1168
1169 }