1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 package org.eclipse.jgit.lib;
53
54 import java.text.MessageFormat;
55 import java.util.ArrayList;
56 import java.util.Collections;
57 import java.util.List;
58 import java.util.Set;
59 import java.util.concurrent.atomic.AtomicReference;
60
61 import org.eclipse.jgit.errors.ConfigInvalidException;
62 import org.eclipse.jgit.events.ConfigChangedEvent;
63 import org.eclipse.jgit.events.ConfigChangedListener;
64 import org.eclipse.jgit.events.ListenerHandle;
65 import org.eclipse.jgit.events.ListenerList;
66 import org.eclipse.jgit.internal.JGitText;
67 import org.eclipse.jgit.util.StringUtils;
68
69
70
71
72
73 public class Config {
74 private static final String[] EMPTY_STRING_ARRAY = {};
75 private static final long KiB = 1024;
76 private static final long MiB = 1024 * KiB;
77 private static final long GiB = 1024 * MiB;
78
79
80 private final ListenerList listeners = new ListenerList();
81
82
83
84
85
86
87
88 private final AtomicReference<ConfigSnapshot> state;
89
90 private final Config baseConfig;
91
92
93
94
95
96
97
98
99 private static final String MAGIC_EMPTY_VALUE = new String();
100
101
102 public Config() {
103 this(null);
104 }
105
106
107
108
109
110
111
112
113 public Config(Config defaultConfig) {
114 baseConfig = defaultConfig;
115 state = new AtomicReference<ConfigSnapshot>(newState());
116 }
117
118
119
120
121
122
123
124
125 private static String escapeValue(final String x) {
126 boolean inquote = false;
127 int lineStart = 0;
128 final StringBuilder r = new StringBuilder(x.length());
129 for (int k = 0; k < x.length(); k++) {
130 final char c = x.charAt(k);
131 switch (c) {
132 case '\n':
133 if (inquote) {
134 r.append('"');
135 inquote = false;
136 }
137 r.append("\\n\\\n");
138 lineStart = r.length();
139 break;
140
141 case '\t':
142 r.append("\\t");
143 break;
144
145 case '\b':
146 r.append("\\b");
147 break;
148
149 case '\\':
150 r.append("\\\\");
151 break;
152
153 case '"':
154 r.append("\\\"");
155 break;
156
157 case ';':
158 case '#':
159 if (!inquote) {
160 r.insert(lineStart, '"');
161 inquote = true;
162 }
163 r.append(c);
164 break;
165
166 case ' ':
167 if (!inquote && r.length() > 0
168 && r.charAt(r.length() - 1) == ' ') {
169 r.insert(lineStart, '"');
170 inquote = true;
171 }
172 r.append(' ');
173 break;
174
175 default:
176 r.append(c);
177 break;
178 }
179 }
180 if (inquote) {
181 r.append('"');
182 }
183 return r.toString();
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197 public int getInt(final String section, final String name,
198 final int defaultValue) {
199 return getInt(section, null, name, defaultValue);
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public int getInt(final String section, String subsection,
216 final String name, final int defaultValue) {
217 final long val = getLong(section, subsection, name, defaultValue);
218 if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE)
219 return (int) val;
220 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().integerValueOutOfRange
221 , section, name));
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235 public long getLong(String section, String name, long defaultValue) {
236 return getLong(section, null, name, defaultValue);
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public long getLong(final String section, String subsection,
253 final String name, final long defaultValue) {
254 final String str = getString(section, subsection, name);
255 if (str == null)
256 return defaultValue;
257
258 String n = str.trim();
259 if (n.length() == 0)
260 return defaultValue;
261
262 long mul = 1;
263 switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
264 case 'g':
265 mul = GiB;
266 break;
267 case 'm':
268 mul = MiB;
269 break;
270 case 'k':
271 mul = KiB;
272 break;
273 }
274 if (mul > 1)
275 n = n.substring(0, n.length() - 1).trim();
276 if (n.length() == 0)
277 return defaultValue;
278
279 try {
280 return mul * Long.parseLong(n);
281 } catch (NumberFormatException nfe) {
282 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidIntegerValue
283 , section, name, str));
284 }
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299 public boolean getBoolean(final String section, final String name,
300 final boolean defaultValue) {
301 return getBoolean(section, null, name, defaultValue);
302 }
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 public boolean getBoolean(final String section, String subsection,
319 final String name, final boolean defaultValue) {
320 String n = getRawString(section, subsection, name);
321 if (n == null)
322 return defaultValue;
323 if (MAGIC_EMPTY_VALUE == n)
324 return true;
325 try {
326 return StringUtils.toBoolean(n);
327 } catch (IllegalArgumentException err) {
328 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidBooleanValue
329 , section, name, n));
330 }
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 public <T extends Enum<?>> T getEnum(final String section,
349 final String subsection, final String name, final T defaultValue) {
350 final T[] all = allValuesOf(defaultValue);
351 return getEnum(all, section, subsection, name, defaultValue);
352 }
353
354 @SuppressWarnings("unchecked")
355 private static <T> T[] allValuesOf(final T value) {
356 try {
357 return (T[]) value.getClass().getMethod("values").invoke(null);
358 } catch (Exception err) {
359 String typeName = value.getClass().getName();
360 String msg = MessageFormat.format(
361 JGitText.get().enumValuesNotAvailable, typeName);
362 throw new IllegalArgumentException(msg, err);
363 }
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 public <T extends Enum<?>> T getEnum(final T[] all, final String section,
385 final String subsection, final String name, final T defaultValue) {
386 String value = getString(section, subsection, name);
387 if (value == null)
388 return defaultValue;
389
390 if (all[0] instanceof ConfigEnum) {
391 for (T t : all) {
392 if (((ConfigEnum) t).matchConfigValue(value))
393 return t;
394 }
395 }
396
397 String n = value.replace(' ', '_');
398
399
400
401
402 n = n.replace('-', '_');
403
404 T trueState = null;
405 T falseState = null;
406 for (T e : all) {
407 if (StringUtils.equalsIgnoreCase(e.name(), n))
408 return e;
409 else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE"))
410 trueState = e;
411 else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE"))
412 falseState = e;
413 }
414
415
416
417
418
419 if (trueState != null && falseState != null) {
420 try {
421 return StringUtils.toBoolean(n) ? trueState : falseState;
422 } catch (IllegalArgumentException err) {
423
424 }
425 }
426
427 if (subsection != null)
428 throw new IllegalArgumentException(MessageFormat.format(
429 JGitText.get().enumValueNotSupported3, section, subsection,
430 name, value));
431 else
432 throw new IllegalArgumentException(
433 MessageFormat.format(JGitText.get().enumValueNotSupported2,
434 section, name, value));
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448 public String getString(final String section, String subsection,
449 final String name) {
450 return getRawString(section, subsection, name);
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 public String[] getStringList(final String section, String subsection,
468 final String name) {
469 String[] base;
470 if (baseConfig != null)
471 base = baseConfig.getStringList(section, subsection, name);
472 else
473 base = EMPTY_STRING_ARRAY;
474
475 String[] self = getRawStringList(section, subsection, name);
476 if (self == null)
477 return base;
478 if (base.length == 0)
479 return self;
480 String[] res = new String[base.length + self.length];
481 int n = base.length;
482 System.arraycopy(base, 0, res, 0, n);
483 System.arraycopy(self, 0, res, n, self.length);
484 return res;
485 }
486
487
488
489
490
491
492
493
494
495
496 public Set<String> getSubsections(final String section) {
497 return getState().getSubsections(section);
498 }
499
500
501
502
503
504
505
506 public Set<String> getSections() {
507 return getState().getSections();
508 }
509
510
511
512
513
514
515 public Set<String> getNames(String section) {
516 return getNames(section, null);
517 }
518
519
520
521
522
523
524
525
526 public Set<String> getNames(String section, String subsection) {
527 return getState().getNames(section, subsection);
528 }
529
530
531
532
533
534
535
536
537
538
539 public Set<String> getNames(String section, boolean recursive) {
540 return getState().getNames(section, null, recursive);
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554 public Set<String> getNames(String section, String subsection,
555 boolean recursive) {
556 return getState().getNames(section, subsection, recursive);
557 }
558
559
560
561
562
563
564
565
566
567
568
569
570
571 @SuppressWarnings("unchecked")
572 public <T> T get(final SectionParser<T> parser) {
573 final ConfigSnapshot myState = getState();
574 T obj = (T) myState.cache.get(parser);
575 if (obj == null) {
576 obj = parser.parse(this);
577 myState.cache.put(parser, obj);
578 }
579 return obj;
580 }
581
582
583
584
585
586
587
588
589
590
591
592 public void uncache(final SectionParser<?> parser) {
593 state.get().cache.remove(parser);
594 }
595
596
597
598
599
600
601
602
603
604
605
606 public ListenerHandle addChangeListener(ConfigChangedListener listener) {
607 return listeners.addConfigChangedListener(listener);
608 }
609
610
611
612
613
614
615
616
617
618
619
620
621
622 protected boolean notifyUponTransientChanges() {
623 return true;
624 }
625
626
627
628
629 protected void fireConfigChangedEvent() {
630 listeners.dispatch(new ConfigChangedEvent());
631 }
632
633 private String getRawString(final String section, final String subsection,
634 final String name) {
635 String[] lst = getRawStringList(section, subsection, name);
636 if (lst != null)
637 return lst[0];
638 else if (baseConfig != null)
639 return baseConfig.getRawString(section, subsection, name);
640 else
641 return null;
642 }
643
644 private String[] getRawStringList(String section, String subsection,
645 String name) {
646 return state.get().get(section, subsection, name);
647 }
648
649 private ConfigSnapshot getState() {
650 ConfigSnapshot cur, upd;
651 do {
652 cur = state.get();
653 final ConfigSnapshot base = getBaseState();
654 if (cur.baseState == base)
655 return cur;
656 upd = new ConfigSnapshot(cur.entryList, base);
657 } while (!state.compareAndSet(cur, upd));
658 return upd;
659 }
660
661 private ConfigSnapshot getBaseState() {
662 return baseConfig != null ? baseConfig.getState() : null;
663 }
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683 public void setInt(final String section, final String subsection,
684 final String name, final int value) {
685 setLong(section, subsection, name, value);
686 }
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706 public void setLong(final String section, final String subsection,
707 final String name, final long value) {
708 final String s;
709
710 if (value >= GiB && (value % GiB) == 0)
711 s = String.valueOf(value / GiB) + " g";
712 else if (value >= MiB && (value % MiB) == 0)
713 s = String.valueOf(value / MiB) + " m";
714 else if (value >= KiB && (value % KiB) == 0)
715 s = String.valueOf(value / KiB) + " k";
716 else
717 s = String.valueOf(value);
718
719 setString(section, subsection, name, s);
720 }
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740 public void setBoolean(final String section, final String subsection,
741 final String name, final boolean value) {
742 setString(section, subsection, name, value ? "true" : "false");
743 }
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765 public <T extends Enum<?>> void setEnum(final String section,
766 final String subsection, final String name, final T value) {
767 String n;
768 if (value instanceof ConfigEnum)
769 n = ((ConfigEnum) value).toConfigValue();
770 else
771 n = value.name().toLowerCase().replace('_', ' ');
772 setString(section, subsection, name, n);
773 }
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793 public void setString(final String section, final String subsection,
794 final String name, final String value) {
795 setStringList(section, subsection, name, Collections
796 .singletonList(value));
797 }
798
799
800
801
802
803
804
805
806
807
808
809 public void unset(final String section, final String subsection,
810 final String name) {
811 setStringList(section, subsection, name, Collections
812 .<String> emptyList());
813 }
814
815
816
817
818
819
820
821
822
823 public void unsetSection(String section, String subsection) {
824 ConfigSnapshot src, res;
825 do {
826 src = state.get();
827 res = unsetSection(src, section, subsection);
828 } while (!state.compareAndSet(src, res));
829 }
830
831 private ConfigSnapshot unsetSection(final ConfigSnapshot srcState,
832 final String section,
833 final String subsection) {
834 final int max = srcState.entryList.size();
835 final ArrayList<ConfigLine> r = new ArrayList<ConfigLine>(max);
836
837 boolean lastWasMatch = false;
838 for (ConfigLine e : srcState.entryList) {
839 if (e.match(section, subsection)) {
840
841 lastWasMatch = true;
842 continue;
843 }
844
845 if (lastWasMatch && e.section == null && e.subsection == null)
846 continue;
847 r.add(e);
848 }
849
850 return newState(r);
851 }
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870 public void setStringList(final String section, final String subsection,
871 final String name, final List<String> values) {
872 ConfigSnapshot src, res;
873 do {
874 src = state.get();
875 res = replaceStringList(src, section, subsection, name, values);
876 } while (!state.compareAndSet(src, res));
877 if (notifyUponTransientChanges())
878 fireConfigChangedEvent();
879 }
880
881 private ConfigSnapshot replaceStringList(final ConfigSnapshot srcState,
882 final String section, final String subsection, final String name,
883 final List<String> values) {
884 final List<ConfigLine> entries = copy(srcState, values);
885 int entryIndex = 0;
886 int valueIndex = 0;
887 int insertPosition = -1;
888
889
890
891 while (entryIndex < entries.size() && valueIndex < values.size()) {
892 final ConfigLine e = entries.get(entryIndex);
893 if (e.match(section, subsection, name)) {
894 entries.set(entryIndex, e.forValue(values.get(valueIndex++)));
895 insertPosition = entryIndex + 1;
896 }
897 entryIndex++;
898 }
899
900
901
902 if (valueIndex == values.size() && entryIndex < entries.size()) {
903 while (entryIndex < entries.size()) {
904 final ConfigLine e = entries.get(entryIndex++);
905 if (e.match(section, subsection, name))
906 entries.remove(--entryIndex);
907 }
908 }
909
910
911
912 if (valueIndex < values.size() && entryIndex == entries.size()) {
913 if (insertPosition < 0) {
914
915
916
917
918 insertPosition = findSectionEnd(entries, section, subsection);
919 }
920 if (insertPosition < 0) {
921
922
923
924 final ConfigLine e = new ConfigLine();
925 e.section = section;
926 e.subsection = subsection;
927 entries.add(e);
928 insertPosition = entries.size();
929 }
930 while (valueIndex < values.size()) {
931 final ConfigLine e = new ConfigLine();
932 e.section = section;
933 e.subsection = subsection;
934 e.name = name;
935 e.value = values.get(valueIndex++);
936 entries.add(insertPosition++, e);
937 }
938 }
939
940 return newState(entries);
941 }
942
943 private static List<ConfigLine> copy(final ConfigSnapshot src,
944 final List<String> values) {
945
946
947
948 final int max = src.entryList.size() + values.size() + 1;
949 final ArrayList<ConfigLine> r = new ArrayList<ConfigLine>(max);
950 r.addAll(src.entryList);
951 return r;
952 }
953
954 private static int findSectionEnd(final List<ConfigLine> entries,
955 final String section, final String subsection) {
956 for (int i = 0; i < entries.size(); i++) {
957 ConfigLine e = entries.get(i);
958 if (e.match(section, subsection, null)) {
959 i++;
960 while (i < entries.size()) {
961 e = entries.get(i);
962 if (e.match(section, subsection, e.name))
963 i++;
964 else
965 break;
966 }
967 return i;
968 }
969 }
970 return -1;
971 }
972
973
974
975
976 public String toText() {
977 final StringBuilder out = new StringBuilder();
978 for (final ConfigLine e : state.get().entryList) {
979 if (e.prefix != null)
980 out.append(e.prefix);
981 if (e.section != null && e.name == null) {
982 out.append('[');
983 out.append(e.section);
984 if (e.subsection != null) {
985 out.append(' ');
986 String escaped = escapeValue(e.subsection);
987
988 boolean quoted = escaped.startsWith("\"")
989 && escaped.endsWith("\"");
990 if (!quoted)
991 out.append('"');
992 out.append(escaped);
993 if (!quoted)
994 out.append('"');
995 }
996 out.append(']');
997 } else if (e.section != null && e.name != null) {
998 if (e.prefix == null || "".equals(e.prefix))
999 out.append('\t');
1000 out.append(e.name);
1001 if (MAGIC_EMPTY_VALUE != e.value) {
1002 out.append(" =");
1003 if (e.value != null) {
1004 out.append(' ');
1005 out.append(escapeValue(e.value));
1006 }
1007 }
1008 if (e.suffix != null)
1009 out.append(' ');
1010 }
1011 if (e.suffix != null)
1012 out.append(e.suffix);
1013 out.append('\n');
1014 }
1015 return out.toString();
1016 }
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027 public void fromText(final String text) throws ConfigInvalidException {
1028 final List<ConfigLine> newEntries = new ArrayList<ConfigLine>();
1029 final StringReader in = new StringReader(text);
1030 ConfigLine last = null;
1031 ConfigLine e = new ConfigLine();
1032 for (;;) {
1033 int input = in.read();
1034 if (-1 == input) {
1035 if (e.section != null)
1036 newEntries.add(e);
1037 break;
1038 }
1039
1040 final char c = (char) input;
1041 if ('\n' == c) {
1042
1043 newEntries.add(e);
1044 if (e.section != null)
1045 last = e;
1046 e = new ConfigLine();
1047
1048 } else if (e.suffix != null) {
1049
1050 e.suffix += c;
1051
1052 } else if (';' == c || '#' == c) {
1053
1054 e.suffix = String.valueOf(c);
1055
1056 } else if (e.section == null && Character.isWhitespace(c)) {
1057
1058 if (e.prefix == null)
1059 e.prefix = "";
1060 e.prefix += c;
1061
1062 } else if ('[' == c) {
1063
1064 e.section = readSectionName(in);
1065 input = in.read();
1066 if ('"' == input) {
1067 e.subsection = readValue(in, true, '"');
1068 input = in.read();
1069 }
1070 if (']' != input)
1071 throw new ConfigInvalidException(JGitText.get().badGroupHeader);
1072 e.suffix = "";
1073
1074 } else if (last != null) {
1075
1076 e.section = last.section;
1077 e.subsection = last.subsection;
1078 in.reset();
1079 e.name = readKeyName(in);
1080 if (e.name.endsWith("\n")) {
1081 e.name = e.name.substring(0, e.name.length() - 1);
1082 e.value = MAGIC_EMPTY_VALUE;
1083 } else
1084 e.value = readValue(in, false, -1);
1085
1086 } else
1087 throw new ConfigInvalidException(JGitText.get().invalidLineInConfigFile);
1088 }
1089
1090 state.set(newState(newEntries));
1091 }
1092
1093 private ConfigSnapshot newState() {
1094 return new ConfigSnapshot(Collections.<ConfigLine> emptyList(),
1095 getBaseState());
1096 }
1097
1098 private ConfigSnapshot newState(final List<ConfigLine> entries) {
1099 return new ConfigSnapshot(Collections.unmodifiableList(entries),
1100 getBaseState());
1101 }
1102
1103
1104
1105
1106 protected void clear() {
1107 state.set(newState());
1108 }
1109
1110 private static String readSectionName(final StringReader in)
1111 throws ConfigInvalidException {
1112 final StringBuilder name = new StringBuilder();
1113 for (;;) {
1114 int c = in.read();
1115 if (c < 0)
1116 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1117
1118 if (']' == c) {
1119 in.reset();
1120 break;
1121 }
1122
1123 if (' ' == c || '\t' == c) {
1124 for (;;) {
1125 c = in.read();
1126 if (c < 0)
1127 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1128
1129 if ('"' == c) {
1130 in.reset();
1131 break;
1132 }
1133
1134 if (' ' == c || '\t' == c)
1135 continue;
1136 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badSectionEntry, name));
1137 }
1138 break;
1139 }
1140
1141 if (Character.isLetterOrDigit((char) c) || '.' == c || '-' == c)
1142 name.append((char) c);
1143 else
1144 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badSectionEntry, name));
1145 }
1146 return name.toString();
1147 }
1148
1149 private static String readKeyName(final StringReader in)
1150 throws ConfigInvalidException {
1151 final StringBuilder name = new StringBuilder();
1152 for (;;) {
1153 int c = in.read();
1154 if (c < 0)
1155 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1156
1157 if ('=' == c)
1158 break;
1159
1160 if (' ' == c || '\t' == c) {
1161 for (;;) {
1162 c = in.read();
1163 if (c < 0)
1164 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1165
1166 if ('=' == c)
1167 break;
1168
1169 if (';' == c || '#' == c || '\n' == c) {
1170 in.reset();
1171 break;
1172 }
1173
1174 if (' ' == c || '\t' == c)
1175 continue;
1176 throw new ConfigInvalidException(JGitText.get().badEntryDelimiter);
1177 }
1178 break;
1179 }
1180
1181 if (Character.isLetterOrDigit((char) c) || c == '-') {
1182
1183
1184
1185 name.append((char) c);
1186 } else if ('\n' == c) {
1187 in.reset();
1188 name.append((char) c);
1189 break;
1190 } else
1191 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badEntryName, name));
1192 }
1193 return name.toString();
1194 }
1195
1196 private static String readValue(final StringReader in, boolean quote,
1197 final int eol) throws ConfigInvalidException {
1198 final StringBuilder value = new StringBuilder();
1199 boolean space = false;
1200 for (;;) {
1201 int c = in.read();
1202 if (c < 0) {
1203 break;
1204 }
1205
1206 if ('\n' == c) {
1207 if (quote)
1208 throw new ConfigInvalidException(JGitText.get().newlineInQuotesNotAllowed);
1209 in.reset();
1210 break;
1211 }
1212
1213 if (eol == c)
1214 break;
1215
1216 if (!quote) {
1217 if (Character.isWhitespace((char) c)) {
1218 space = true;
1219 continue;
1220 }
1221 if (';' == c || '#' == c) {
1222 in.reset();
1223 break;
1224 }
1225 }
1226
1227 if (space) {
1228 if (value.length() > 0)
1229 value.append(' ');
1230 space = false;
1231 }
1232
1233 if ('\\' == c) {
1234 c = in.read();
1235 switch (c) {
1236 case -1:
1237 throw new ConfigInvalidException(JGitText.get().endOfFileInEscape);
1238 case '\n':
1239 continue;
1240 case 't':
1241 value.append('\t');
1242 continue;
1243 case 'b':
1244 value.append('\b');
1245 continue;
1246 case 'n':
1247 value.append('\n');
1248 continue;
1249 case '\\':
1250 value.append('\\');
1251 continue;
1252 case '"':
1253 value.append('"');
1254 continue;
1255 default:
1256 throw new ConfigInvalidException(MessageFormat.format(
1257 JGitText.get().badEscape,
1258 Character.valueOf(((char) c))));
1259 }
1260 }
1261
1262 if ('"' == c) {
1263 quote = !quote;
1264 continue;
1265 }
1266
1267 value.append((char) c);
1268 }
1269 return value.length() > 0 ? value.toString() : null;
1270 }
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286 public static interface SectionParser<T> {
1287
1288
1289
1290
1291
1292
1293
1294 T parse(Config cfg);
1295 }
1296
1297 private static class StringReader {
1298 private final char[] buf;
1299
1300 private int pos;
1301
1302 StringReader(final String in) {
1303 buf = in.toCharArray();
1304 }
1305
1306 int read() {
1307 try {
1308 return buf[pos++];
1309 } catch (ArrayIndexOutOfBoundsException e) {
1310 pos = buf.length;
1311 return -1;
1312 }
1313 }
1314
1315 void reset() {
1316 pos--;
1317 }
1318 }
1319
1320
1321
1322
1323
1324
1325 public static interface ConfigEnum {
1326
1327
1328
1329
1330
1331 String toConfigValue();
1332
1333
1334
1335
1336
1337
1338
1339
1340 boolean matchConfigValue(String in);
1341 }
1342 }