1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration;
19
20 import java.lang.reflect.Array;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.NoSuchElementException;
30 import java.util.Properties;
31
32 import org.apache.commons.configuration.event.ConfigurationErrorEvent;
33 import org.apache.commons.configuration.event.ConfigurationErrorListener;
34 import org.apache.commons.configuration.event.EventSource;
35 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
36 import org.apache.commons.lang.BooleanUtils;
37 import org.apache.commons.lang.ClassUtils;
38 import org.apache.commons.lang.ObjectUtils;
39 import org.apache.commons.lang.text.StrLookup;
40 import org.apache.commons.lang.text.StrSubstitutor;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.impl.NoOpLog;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public abstract class AbstractConfiguration extends EventSource implements Configuration
82 {
83
84
85
86
87 public static final int EVENT_ADD_PROPERTY = 1;
88
89
90
91
92
93 public static final int EVENT_CLEAR_PROPERTY = 2;
94
95
96
97
98
99 public static final int EVENT_SET_PROPERTY = 3;
100
101
102
103
104
105 public static final int EVENT_CLEAR = 4;
106
107
108
109
110
111
112 public static final int EVENT_READ_PROPERTY = 5;
113
114
115 protected static final String START_TOKEN = "${";
116
117
118 protected static final String END_TOKEN = "}";
119
120
121
122
123
124
125 private static final char DISABLED_DELIMITER = '\0';
126
127
128 private static char defaultListDelimiter = ',';
129
130
131 private char listDelimiter = defaultListDelimiter;
132
133
134
135
136
137 private boolean delimiterParsingDisabled;
138
139
140
141
142
143 private boolean throwExceptionOnMissing;
144
145
146 private StrSubstitutor substitutor;
147
148
149 private Log log;
150
151
152
153
154 public AbstractConfiguration()
155 {
156 setLogger(null);
157 }
158
159
160
161
162
163
164
165
166
167 public static void setDefaultListDelimiter(char delimiter)
168 {
169 AbstractConfiguration.defaultListDelimiter = delimiter;
170 }
171
172
173
174
175
176
177
178
179 @Deprecated
180 public static void setDelimiter(char delimiter)
181 {
182 setDefaultListDelimiter(delimiter);
183 }
184
185
186
187
188
189
190 public static char getDefaultListDelimiter()
191 {
192 return AbstractConfiguration.defaultListDelimiter;
193 }
194
195
196
197
198
199
200
201 @Deprecated
202 public static char getDelimiter()
203 {
204 return getDefaultListDelimiter();
205 }
206
207
208
209
210
211
212
213
214
215
216 public void setListDelimiter(char listDelimiter)
217 {
218 this.listDelimiter = listDelimiter;
219 }
220
221
222
223
224
225
226
227 public char getListDelimiter()
228 {
229 return listDelimiter;
230 }
231
232
233
234
235
236
237 public boolean isDelimiterParsingDisabled()
238 {
239 return delimiterParsingDisabled;
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253 public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
254 {
255 this.delimiterParsingDisabled = delimiterParsingDisabled;
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269 public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
270 {
271 this.throwExceptionOnMissing = throwExceptionOnMissing;
272 }
273
274
275
276
277
278
279 public boolean isThrowExceptionOnMissing()
280 {
281 return throwExceptionOnMissing;
282 }
283
284
285
286
287
288
289
290 public synchronized StrSubstitutor getSubstitutor()
291 {
292 if (substitutor == null)
293 {
294 substitutor = new StrSubstitutor(createInterpolator());
295 }
296 return substitutor;
297 }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 public ConfigurationInterpolator getInterpolator()
313 {
314 return (ConfigurationInterpolator) getSubstitutor()
315 .getVariableResolver();
316 }
317
318
319
320
321
322
323
324
325
326
327
328 protected ConfigurationInterpolator createInterpolator()
329 {
330 ConfigurationInterpolator interpol = new ConfigurationInterpolator();
331 interpol.setDefaultLookup(new StrLookup()
332 {
333 @Override
334 public String lookup(String var)
335 {
336 Object prop = resolveContainerStore(var);
337 return (prop != null) ? prop.toString() : null;
338 }
339 });
340 return interpol;
341 }
342
343
344
345
346
347
348
349 public Log getLogger()
350 {
351 return log;
352 }
353
354
355
356
357
358
359
360
361
362
363
364 public void setLogger(Log log)
365 {
366 this.log = (log != null) ? log : new NoOpLog();
367 }
368
369
370
371
372
373
374
375
376
377
378
379 public void addErrorLogListener()
380 {
381 addErrorListener(new ConfigurationErrorListener()
382 {
383 public void configurationError(ConfigurationErrorEvent event)
384 {
385 getLogger().warn("Internal error", event.getCause());
386 }
387 });
388 }
389
390 public void addProperty(String key, Object value)
391 {
392 fireEvent(EVENT_ADD_PROPERTY, key, value, true);
393 addPropertyValues(key, value,
394 isDelimiterParsingDisabled() ? DISABLED_DELIMITER
395 : getListDelimiter());
396 fireEvent(EVENT_ADD_PROPERTY, key, value, false);
397 }
398
399
400
401
402
403
404
405
406 protected abstract void addPropertyDirect(String key, Object value);
407
408
409
410
411
412
413
414
415
416
417
418 private void addPropertyValues(String key, Object value, char delimiter)
419 {
420 Iterator<?> it = PropertyConverter.toIterator(value, delimiter);
421 while (it.hasNext())
422 {
423 addPropertyDirect(key, it.next());
424 }
425 }
426
427
428
429
430
431
432
433
434 protected String interpolate(String base)
435 {
436 Object result = interpolate((Object) base);
437 return (result == null) ? null : result.toString();
438 }
439
440
441
442
443
444
445
446
447 protected Object interpolate(Object value)
448 {
449 return PropertyConverter.interpolate(value, this);
450 }
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468 @Deprecated
469 protected String interpolateHelper(String base, List<?> priorVariables)
470 {
471 return base;
472 }
473
474 public Configuration subset(String prefix)
475 {
476 return new SubsetConfiguration(this, prefix, ".");
477 }
478
479 public void setProperty(String key, Object value)
480 {
481 fireEvent(EVENT_SET_PROPERTY, key, value, true);
482 setDetailEvents(false);
483 try
484 {
485 clearProperty(key);
486 addProperty(key, value);
487 }
488 finally
489 {
490 setDetailEvents(true);
491 }
492 fireEvent(EVENT_SET_PROPERTY, key, value, false);
493 }
494
495
496
497
498
499
500
501
502 public void clearProperty(String key)
503 {
504 fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
505 clearPropertyDirect(key);
506 fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
507 }
508
509
510
511
512
513
514
515
516
517 protected void clearPropertyDirect(String key)
518 {
519
520 }
521
522 public void clear()
523 {
524 fireEvent(EVENT_CLEAR, null, null, true);
525 setDetailEvents(false);
526 boolean useIterator = true;
527 try
528 {
529 Iterator<String> it = getKeys();
530 while (it.hasNext())
531 {
532 String key = it.next();
533 if (useIterator)
534 {
535 try
536 {
537 it.remove();
538 }
539 catch (UnsupportedOperationException usoex)
540 {
541 useIterator = false;
542 }
543 }
544
545 if (useIterator && containsKey(key))
546 {
547 useIterator = false;
548 }
549
550 if (!useIterator)
551 {
552
553
554 clearProperty(key);
555 }
556 }
557 }
558 finally
559 {
560 setDetailEvents(true);
561 }
562 fireEvent(EVENT_CLEAR, null, null, false);
563 }
564
565
566
567
568
569
570
571
572 public Iterator<String> getKeys(String prefix)
573 {
574 return new PrefixedKeysIterator(getKeys(), prefix);
575 }
576
577 public Properties getProperties(String key)
578 {
579 return getProperties(key, null);
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597 public Properties getProperties(String key, Properties defaults)
598 {
599
600
601
602 String[] tokens = getStringArray(key);
603
604
605
606
607 Properties props = defaults == null ? new Properties() : new Properties(defaults);
608 for (String token : tokens)
609 {
610 int equalSign = token.indexOf('=');
611 if (equalSign > 0)
612 {
613 String pkey = token.substring(0, equalSign).trim();
614 String pvalue = token.substring(equalSign + 1).trim();
615 props.put(pkey, pvalue);
616 }
617 else if (tokens.length == 1 && "".equals(token))
618 {
619
620
621 break;
622 }
623 else
624 {
625 throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign");
626 }
627 }
628 return props;
629 }
630
631
632
633
634
635 public boolean getBoolean(String key)
636 {
637 Boolean b = getBoolean(key, null);
638 if (b != null)
639 {
640 return b.booleanValue();
641 }
642 else
643 {
644 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
645 }
646 }
647
648
649
650
651
652 public boolean getBoolean(String key, boolean defaultValue)
653 {
654 return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue();
655 }
656
657
658
659
660
661
662
663
664
665
666
667
668
669 public Boolean getBoolean(String key, Boolean defaultValue)
670 {
671 Object value = resolveContainerStore(key);
672
673 if (value == null)
674 {
675 return defaultValue;
676 }
677 else
678 {
679 try
680 {
681 return PropertyConverter.toBoolean(interpolate(value));
682 }
683 catch (ConversionException e)
684 {
685 throw new ConversionException('\'' + key + "' doesn't map to a Boolean object", e);
686 }
687 }
688 }
689
690 public byte getByte(String key)
691 {
692 Byte b = getByte(key, null);
693 if (b != null)
694 {
695 return b.byteValue();
696 }
697 else
698 {
699 throw new NoSuchElementException('\'' + key + " doesn't map to an existing object");
700 }
701 }
702
703 public byte getByte(String key, byte defaultValue)
704 {
705 return getByte(key, new Byte(defaultValue)).byteValue();
706 }
707
708 public Byte getByte(String key, Byte defaultValue)
709 {
710 Object value = resolveContainerStore(key);
711
712 if (value == null)
713 {
714 return defaultValue;
715 }
716 else
717 {
718 try
719 {
720 return PropertyConverter.toByte(interpolate(value));
721 }
722 catch (ConversionException e)
723 {
724 throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e);
725 }
726 }
727 }
728
729 public double getDouble(String key)
730 {
731 Double d = getDouble(key, null);
732 if (d != null)
733 {
734 return d.doubleValue();
735 }
736 else
737 {
738 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
739 }
740 }
741
742 public double getDouble(String key, double defaultValue)
743 {
744 return getDouble(key, new Double(defaultValue)).doubleValue();
745 }
746
747 public Double getDouble(String key, Double defaultValue)
748 {
749 Object value = resolveContainerStore(key);
750
751 if (value == null)
752 {
753 return defaultValue;
754 }
755 else
756 {
757 try
758 {
759 return PropertyConverter.toDouble(interpolate(value));
760 }
761 catch (ConversionException e)
762 {
763 throw new ConversionException('\'' + key + "' doesn't map to a Double object", e);
764 }
765 }
766 }
767
768 public float getFloat(String key)
769 {
770 Float f = getFloat(key, null);
771 if (f != null)
772 {
773 return f.floatValue();
774 }
775 else
776 {
777 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
778 }
779 }
780
781 public float getFloat(String key, float defaultValue)
782 {
783 return getFloat(key, new Float(defaultValue)).floatValue();
784 }
785
786 public Float getFloat(String key, Float defaultValue)
787 {
788 Object value = resolveContainerStore(key);
789
790 if (value == null)
791 {
792 return defaultValue;
793 }
794 else
795 {
796 try
797 {
798 return PropertyConverter.toFloat(interpolate(value));
799 }
800 catch (ConversionException e)
801 {
802 throw new ConversionException('\'' + key + "' doesn't map to a Float object", e);
803 }
804 }
805 }
806
807 public int getInt(String key)
808 {
809 Integer i = getInteger(key, null);
810 if (i != null)
811 {
812 return i.intValue();
813 }
814 else
815 {
816 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
817 }
818 }
819
820 public int getInt(String key, int defaultValue)
821 {
822 Integer i = getInteger(key, null);
823
824 if (i == null)
825 {
826 return defaultValue;
827 }
828
829 return i.intValue();
830 }
831
832 public Integer getInteger(String key, Integer defaultValue)
833 {
834 Object value = resolveContainerStore(key);
835
836 if (value == null)
837 {
838 return defaultValue;
839 }
840 else
841 {
842 try
843 {
844 return PropertyConverter.toInteger(interpolate(value));
845 }
846 catch (ConversionException e)
847 {
848 throw new ConversionException('\'' + key + "' doesn't map to an Integer object", e);
849 }
850 }
851 }
852
853 public long getLong(String key)
854 {
855 Long l = getLong(key, null);
856 if (l != null)
857 {
858 return l.longValue();
859 }
860 else
861 {
862 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
863 }
864 }
865
866 public long getLong(String key, long defaultValue)
867 {
868 return getLong(key, new Long(defaultValue)).longValue();
869 }
870
871 public Long getLong(String key, Long defaultValue)
872 {
873 Object value = resolveContainerStore(key);
874
875 if (value == null)
876 {
877 return defaultValue;
878 }
879 else
880 {
881 try
882 {
883 return PropertyConverter.toLong(interpolate(value));
884 }
885 catch (ConversionException e)
886 {
887 throw new ConversionException('\'' + key + "' doesn't map to a Long object", e);
888 }
889 }
890 }
891
892 public short getShort(String key)
893 {
894 Short s = getShort(key, null);
895 if (s != null)
896 {
897 return s.shortValue();
898 }
899 else
900 {
901 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
902 }
903 }
904
905 public short getShort(String key, short defaultValue)
906 {
907 return getShort(key, new Short(defaultValue)).shortValue();
908 }
909
910 public Short getShort(String key, Short defaultValue)
911 {
912 Object value = resolveContainerStore(key);
913
914 if (value == null)
915 {
916 return defaultValue;
917 }
918 else
919 {
920 try
921 {
922 return PropertyConverter.toShort(interpolate(value));
923 }
924 catch (ConversionException e)
925 {
926 throw new ConversionException('\'' + key + "' doesn't map to a Short object", e);
927 }
928 }
929 }
930
931
932
933
934
935 public BigDecimal getBigDecimal(String key)
936 {
937 BigDecimal number = getBigDecimal(key, null);
938 if (number != null)
939 {
940 return number;
941 }
942 else if (isThrowExceptionOnMissing())
943 {
944 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
945 }
946 else
947 {
948 return null;
949 }
950 }
951
952 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
953 {
954 Object value = resolveContainerStore(key);
955
956 if (value == null)
957 {
958 return defaultValue;
959 }
960 else
961 {
962 try
963 {
964 return PropertyConverter.toBigDecimal(interpolate(value));
965 }
966 catch (ConversionException e)
967 {
968 throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e);
969 }
970 }
971 }
972
973
974
975
976
977 public BigInteger getBigInteger(String key)
978 {
979 BigInteger number = getBigInteger(key, null);
980 if (number != null)
981 {
982 return number;
983 }
984 else if (isThrowExceptionOnMissing())
985 {
986 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
987 }
988 else
989 {
990 return null;
991 }
992 }
993
994 public BigInteger getBigInteger(String key, BigInteger defaultValue)
995 {
996 Object value = resolveContainerStore(key);
997
998 if (value == null)
999 {
1000 return defaultValue;
1001 }
1002 else
1003 {
1004 try
1005 {
1006 return PropertyConverter.toBigInteger(interpolate(value));
1007 }
1008 catch (ConversionException e)
1009 {
1010 throw new ConversionException('\'' + key + "' doesn't map to a BigInteger object", e);
1011 }
1012 }
1013 }
1014
1015
1016
1017
1018
1019 public String getString(String key)
1020 {
1021 String s = getString(key, null);
1022 if (s != null)
1023 {
1024 return s;
1025 }
1026 else if (isThrowExceptionOnMissing())
1027 {
1028 throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object");
1029 }
1030 else
1031 {
1032 return null;
1033 }
1034 }
1035
1036 public String getString(String key, String defaultValue)
1037 {
1038 Object value = resolveContainerStore(key);
1039
1040 if (value instanceof String)
1041 {
1042 return interpolate((String) value);
1043 }
1044 else if (value == null)
1045 {
1046 return interpolate(defaultValue);
1047 }
1048 else
1049 {
1050 throw new ConversionException('\'' + key + "' doesn't map to a String object");
1051 }
1052 }
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 public String[] getStringArray(String key)
1074 {
1075 Object value = getProperty(key);
1076
1077 String[] array;
1078
1079 if (value instanceof String)
1080 {
1081 array = new String[1];
1082
1083 array[0] = interpolate((String) value);
1084 }
1085 else if (value instanceof List)
1086 {
1087 List<?> list = (List<?>) value;
1088 array = new String[list.size()];
1089
1090 for (int i = 0; i < array.length; i++)
1091 {
1092 array[i] = interpolate(ObjectUtils.toString(list.get(i), null));
1093 }
1094 }
1095 else if (value == null)
1096 {
1097 array = new String[0];
1098 }
1099 else if (isScalarValue(value))
1100 {
1101 array = new String[1];
1102 array[0] = value.toString();
1103 }
1104 else
1105 {
1106 throw new ConversionException('\'' + key + "' doesn't map to a String/List object");
1107 }
1108 return array;
1109 }
1110
1111
1112
1113
1114
1115 public List<Object> getList(String key)
1116 {
1117 return getList(key, new ArrayList<Object>());
1118 }
1119
1120 public List<Object> getList(String key, List<Object> defaultValue)
1121 {
1122 Object value = getProperty(key);
1123 List<Object> list;
1124
1125 if (value instanceof String)
1126 {
1127 list = new ArrayList<Object>(1);
1128 list.add(interpolate((String) value));
1129 }
1130 else if (value instanceof List)
1131 {
1132 list = new ArrayList<Object>();
1133 List<?> l = (List<?>) value;
1134
1135
1136 for (Object elem : l)
1137 {
1138 list.add(interpolate(elem));
1139 }
1140 }
1141 else if (value == null)
1142 {
1143 list = defaultValue;
1144 }
1145 else if (value.getClass().isArray())
1146 {
1147 return Arrays.asList((Object[]) value);
1148 }
1149 else if (isScalarValue(value))
1150 {
1151 return Collections.singletonList((Object) value.toString());
1152 }
1153 else
1154 {
1155 throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a "
1156 + value.getClass().getName());
1157 }
1158 return list;
1159 }
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169 protected Object resolveContainerStore(String key)
1170 {
1171 Object value = getProperty(key);
1172 if (value != null)
1173 {
1174 if (value instanceof Collection)
1175 {
1176 Collection<?> collection = (Collection<?>) value;
1177 value = collection.isEmpty() ? null : collection.iterator().next();
1178 }
1179 else if (value.getClass().isArray() && Array.getLength(value) > 0)
1180 {
1181 value = Array.get(value, 0);
1182 }
1183 }
1184
1185 return value;
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 protected boolean isScalarValue(Object value)
1202 {
1203 return ClassUtils.wrapperToPrimitive(value.getClass()) != null;
1204 }
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 public void copy(Configuration c)
1222 {
1223 if (c != null)
1224 {
1225 for (Iterator<String> it = c.getKeys(); it.hasNext();)
1226 {
1227 String key = it.next();
1228 Object value = c.getProperty(key);
1229 fireEvent(EVENT_SET_PROPERTY, key, value, true);
1230 setDetailEvents(false);
1231 try
1232 {
1233 clearProperty(key);
1234 addPropertyValues(key, value, DISABLED_DELIMITER);
1235 }
1236 finally
1237 {
1238 setDetailEvents(true);
1239 }
1240 fireEvent(EVENT_SET_PROPERTY, key, value, false);
1241 }
1242 }
1243 }
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262 public void append(Configuration c)
1263 {
1264 if (c != null)
1265 {
1266 for (Iterator<String> it = c.getKeys(); it.hasNext();)
1267 {
1268 String key = it.next();
1269 Object value = c.getProperty(key);
1270 fireEvent(EVENT_ADD_PROPERTY, key, value, true);
1271 addPropertyValues(key, value, DISABLED_DELIMITER);
1272 fireEvent(EVENT_ADD_PROPERTY, key, value, false);
1273 }
1274 }
1275 }
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292 public Configuration interpolatedConfiguration()
1293 {
1294
1295 AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils
1296 .cloneConfiguration(this);
1297
1298
1299 c.setDelimiterParsingDisabled(true);
1300 for (Iterator<String> it = getKeys(); it.hasNext();)
1301 {
1302 String key = it.next();
1303 c.setProperty(key, getList(key));
1304 }
1305
1306 c.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
1307 return c;
1308 }
1309 }