1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.time.format;
17
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.joda.time.Chronology;
29 import org.joda.time.DateTimeConstants;
30 import org.joda.time.DateTimeField;
31 import org.joda.time.DateTimeFieldType;
32 import org.joda.time.DateTimeUtils;
33 import org.joda.time.DateTimeZone;
34 import org.joda.time.MutableDateTime;
35 import org.joda.time.ReadablePartial;
36 import org.joda.time.MutableDateTime.Property;
37 import org.joda.time.field.MillisDurationField;
38 import org.joda.time.field.PreciseDateTimeField;
39
40
41
42
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 public class DateTimeFormatterBuilder {
73
74
75 private ArrayList<Object> iElementPairs;
76
77 private Object iFormatter;
78
79
80
81
82
83 public DateTimeFormatterBuilder() {
84 super();
85 iElementPairs = new ArrayList<Object>();
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public DateTimeFormatter toFormatter() {
105 Object f = getFormatter();
106 DateTimePrinter printer = null;
107 if (isPrinter(f)) {
108 printer = (DateTimePrinter) f;
109 }
110 DateTimeParser parser = null;
111 if (isParser(f)) {
112 parser = (DateTimeParser) f;
113 }
114 if (printer != null || parser != null) {
115 return new DateTimeFormatter(printer, parser);
116 }
117 throw new UnsupportedOperationException("Both printing and parsing not supported");
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public DateTimePrinter toPrinter() {
133 Object f = getFormatter();
134 if (isPrinter(f)) {
135 return (DateTimePrinter) f;
136 }
137 throw new UnsupportedOperationException("Printing is not supported");
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152 public DateTimeParser toParser() {
153 Object f = getFormatter();
154 if (isParser(f)) {
155 return (DateTimeParser) f;
156 }
157 throw new UnsupportedOperationException("Parsing is not supported");
158 }
159
160
161
162
163
164
165
166
167 public boolean canBuildFormatter() {
168 return isFormatter(getFormatter());
169 }
170
171
172
173
174
175
176
177 public boolean canBuildPrinter() {
178 return isPrinter(getFormatter());
179 }
180
181
182
183
184
185
186
187 public boolean canBuildParser() {
188 return isParser(getFormatter());
189 }
190
191
192
193
194
195
196 public void clear() {
197 iFormatter = null;
198 iElementPairs.clear();
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public DateTimeFormatterBuilder append(DateTimeFormatter formatter) {
217 if (formatter == null) {
218 throw new IllegalArgumentException("No formatter supplied");
219 }
220 return append0(formatter.getPrinter(), formatter.getParser());
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237 public DateTimeFormatterBuilder append(DateTimePrinter printer) {
238 checkPrinter(printer);
239 return append0(printer, null);
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256 public DateTimeFormatterBuilder append(DateTimeParser parser) {
257 checkParser(parser);
258 return append0(null, parser);
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 public DateTimeFormatterBuilder append(DateTimePrinter printer, DateTimeParser parser) {
276 checkPrinter(printer);
277 checkParser(parser);
278 return append0(printer, parser);
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304 public DateTimeFormatterBuilder append(DateTimePrinter printer, DateTimeParser[] parsers) {
305 if (printer != null) {
306 checkPrinter(printer);
307 }
308 if (parsers == null) {
309 throw new IllegalArgumentException("No parsers supplied");
310 }
311 int length = parsers.length;
312 if (length == 1) {
313 if (parsers[0] == null) {
314 throw new IllegalArgumentException("No parser supplied");
315 }
316 return append0(printer, parsers[0]);
317 }
318
319 DateTimeParser[] copyOfParsers = new DateTimeParser[length];
320 int i;
321 for (i = 0; i < length - 1; i++) {
322 if ((copyOfParsers[i] = parsers[i]) == null) {
323 throw new IllegalArgumentException("Incomplete parser array");
324 }
325 }
326 copyOfParsers[i] = parsers[i];
327
328 return append0(printer, new MatchingParser(copyOfParsers));
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 public DateTimeFormatterBuilder appendOptional(DateTimeParser parser) {
345 checkParser(parser);
346 DateTimeParser[] parsers = new DateTimeParser[] {parser, null};
347 return append0(null, new MatchingParser(parsers));
348 }
349
350
351
352
353
354
355
356 private void checkParser(DateTimeParser parser) {
357 if (parser == null) {
358 throw new IllegalArgumentException("No parser supplied");
359 }
360 }
361
362
363
364
365
366
367 private void checkPrinter(DateTimePrinter printer) {
368 if (printer == null) {
369 throw new IllegalArgumentException("No printer supplied");
370 }
371 }
372
373 private DateTimeFormatterBuilder append0(Object element) {
374 iFormatter = null;
375
376 iElementPairs.add(element);
377 iElementPairs.add(element);
378 return this;
379 }
380
381 private DateTimeFormatterBuilder append0(
382 DateTimePrinter printer, DateTimeParser parser) {
383 iFormatter = null;
384 iElementPairs.add(printer);
385 iElementPairs.add(parser);
386 return this;
387 }
388
389
390
391
392
393
394
395
396 public DateTimeFormatterBuilder appendLiteral(char c) {
397 return append0(new CharacterLiteral(c));
398 }
399
400
401
402
403
404
405
406
407 public DateTimeFormatterBuilder appendLiteral(String text) {
408 if (text == null) {
409 throw new IllegalArgumentException("Literal must not be null");
410 }
411 switch (text.length()) {
412 case 0:
413 return this;
414 case 1:
415 return append0(new CharacterLiteral(text.charAt(0)));
416 default:
417 return append0(new StringLiteral(text));
418 }
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432 public DateTimeFormatterBuilder appendDecimal(
433 DateTimeFieldType fieldType, int minDigits, int maxDigits) {
434 if (fieldType == null) {
435 throw new IllegalArgumentException("Field type must not be null");
436 }
437 if (maxDigits < minDigits) {
438 maxDigits = minDigits;
439 }
440 if (minDigits < 0 || maxDigits <= 0) {
441 throw new IllegalArgumentException();
442 }
443 if (minDigits <= 1) {
444 return append0(new UnpaddedNumber(fieldType, maxDigits, false));
445 } else {
446 return append0(new PaddedNumber(fieldType, maxDigits, false, minDigits));
447 }
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462 public DateTimeFormatterBuilder appendFixedDecimal(
463 DateTimeFieldType fieldType, int numDigits) {
464 if (fieldType == null) {
465 throw new IllegalArgumentException("Field type must not be null");
466 }
467 if (numDigits <= 0) {
468 throw new IllegalArgumentException("Illegal number of digits: " + numDigits);
469 }
470 return append0(new FixedNumber(fieldType, numDigits, false));
471 }
472
473
474
475
476
477
478
479
480
481
482
483
484 public DateTimeFormatterBuilder appendSignedDecimal(
485 DateTimeFieldType fieldType, int minDigits, int maxDigits) {
486 if (fieldType == null) {
487 throw new IllegalArgumentException("Field type must not be null");
488 }
489 if (maxDigits < minDigits) {
490 maxDigits = minDigits;
491 }
492 if (minDigits < 0 || maxDigits <= 0) {
493 throw new IllegalArgumentException();
494 }
495 if (minDigits <= 1) {
496 return append0(new UnpaddedNumber(fieldType, maxDigits, true));
497 } else {
498 return append0(new PaddedNumber(fieldType, maxDigits, true, minDigits));
499 }
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513
514 public DateTimeFormatterBuilder appendFixedSignedDecimal(
515 DateTimeFieldType fieldType, int numDigits) {
516 if (fieldType == null) {
517 throw new IllegalArgumentException("Field type must not be null");
518 }
519 if (numDigits <= 0) {
520 throw new IllegalArgumentException("Illegal number of digits: " + numDigits);
521 }
522 return append0(new FixedNumber(fieldType, numDigits, true));
523 }
524
525
526
527
528
529
530
531
532
533 public DateTimeFormatterBuilder appendText(DateTimeFieldType fieldType) {
534 if (fieldType == null) {
535 throw new IllegalArgumentException("Field type must not be null");
536 }
537 return append0(new TextField(fieldType, false));
538 }
539
540
541
542
543
544
545
546
547
548 public DateTimeFormatterBuilder appendShortText(DateTimeFieldType fieldType) {
549 if (fieldType == null) {
550 throw new IllegalArgumentException("Field type must not be null");
551 }
552 return append0(new TextField(fieldType, true));
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568 public DateTimeFormatterBuilder appendFraction(
569 DateTimeFieldType fieldType, int minDigits, int maxDigits) {
570 if (fieldType == null) {
571 throw new IllegalArgumentException("Field type must not be null");
572 }
573 if (maxDigits < minDigits) {
574 maxDigits = minDigits;
575 }
576 if (minDigits < 0 || maxDigits <= 0) {
577 throw new IllegalArgumentException();
578 }
579 return append0(new Fraction(fieldType, minDigits, maxDigits));
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596 public DateTimeFormatterBuilder appendFractionOfSecond(int minDigits, int maxDigits) {
597 return appendFraction(DateTimeFieldType.secondOfDay(), minDigits, maxDigits);
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613 public DateTimeFormatterBuilder appendFractionOfMinute(int minDigits, int maxDigits) {
614 return appendFraction(DateTimeFieldType.minuteOfDay(), minDigits, maxDigits);
615 }
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630 public DateTimeFormatterBuilder appendFractionOfHour(int minDigits, int maxDigits) {
631 return appendFraction(DateTimeFieldType.hourOfDay(), minDigits, maxDigits);
632 }
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647 public DateTimeFormatterBuilder appendFractionOfDay(int minDigits, int maxDigits) {
648 return appendFraction(DateTimeFieldType.dayOfYear(), minDigits, maxDigits);
649 }
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664 public DateTimeFormatterBuilder appendMillisOfSecond(int minDigits) {
665 return appendDecimal(DateTimeFieldType.millisOfSecond(), minDigits, 3);
666 }
667
668
669
670
671
672
673
674 public DateTimeFormatterBuilder appendMillisOfDay(int minDigits) {
675 return appendDecimal(DateTimeFieldType.millisOfDay(), minDigits, 8);
676 }
677
678
679
680
681
682
683
684 public DateTimeFormatterBuilder appendSecondOfMinute(int minDigits) {
685 return appendDecimal(DateTimeFieldType.secondOfMinute(), minDigits, 2);
686 }
687
688
689
690
691
692
693
694 public DateTimeFormatterBuilder appendSecondOfDay(int minDigits) {
695 return appendDecimal(DateTimeFieldType.secondOfDay(), minDigits, 5);
696 }
697
698
699
700
701
702
703
704 public DateTimeFormatterBuilder appendMinuteOfHour(int minDigits) {
705 return appendDecimal(DateTimeFieldType.minuteOfHour(), minDigits, 2);
706 }
707
708
709
710
711
712
713
714 public DateTimeFormatterBuilder appendMinuteOfDay(int minDigits) {
715 return appendDecimal(DateTimeFieldType.minuteOfDay(), minDigits, 4);
716 }
717
718
719
720
721
722
723
724 public DateTimeFormatterBuilder appendHourOfDay(int minDigits) {
725 return appendDecimal(DateTimeFieldType.hourOfDay(), minDigits, 2);
726 }
727
728
729
730
731
732
733
734 public DateTimeFormatterBuilder appendClockhourOfDay(int minDigits) {
735 return appendDecimal(DateTimeFieldType.clockhourOfDay(), minDigits, 2);
736 }
737
738
739
740
741
742
743
744 public DateTimeFormatterBuilder appendHourOfHalfday(int minDigits) {
745 return appendDecimal(DateTimeFieldType.hourOfHalfday(), minDigits, 2);
746 }
747
748
749
750
751
752
753
754 public DateTimeFormatterBuilder appendClockhourOfHalfday(int minDigits) {
755 return appendDecimal(DateTimeFieldType.clockhourOfHalfday(), minDigits, 2);
756 }
757
758
759
760
761
762
763
764 public DateTimeFormatterBuilder appendDayOfWeek(int minDigits) {
765 return appendDecimal(DateTimeFieldType.dayOfWeek(), minDigits, 1);
766 }
767
768
769
770
771
772
773
774 public DateTimeFormatterBuilder appendDayOfMonth(int minDigits) {
775 return appendDecimal(DateTimeFieldType.dayOfMonth(), minDigits, 2);
776 }
777
778
779
780
781
782
783
784 public DateTimeFormatterBuilder appendDayOfYear(int minDigits) {
785 return appendDecimal(DateTimeFieldType.dayOfYear(), minDigits, 3);
786 }
787
788
789
790
791
792
793
794 public DateTimeFormatterBuilder appendWeekOfWeekyear(int minDigits) {
795 return appendDecimal(DateTimeFieldType.weekOfWeekyear(), minDigits, 2);
796 }
797
798
799
800
801
802
803
804
805
806 public DateTimeFormatterBuilder appendWeekyear(int minDigits, int maxDigits) {
807 return appendSignedDecimal(DateTimeFieldType.weekyear(), minDigits, maxDigits);
808 }
809
810
811
812
813
814
815
816 public DateTimeFormatterBuilder appendMonthOfYear(int minDigits) {
817 return appendDecimal(DateTimeFieldType.monthOfYear(), minDigits, 2);
818 }
819
820
821
822
823
824
825
826
827
828 public DateTimeFormatterBuilder appendYear(int minDigits, int maxDigits) {
829 return appendSignedDecimal(DateTimeFieldType.year(), minDigits, maxDigits);
830 }
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850 public DateTimeFormatterBuilder appendTwoDigitYear(int pivot) {
851 return appendTwoDigitYear(pivot, false);
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868 public DateTimeFormatterBuilder appendTwoDigitYear(int pivot, boolean lenientParse) {
869 return append0(new TwoDigitYear(DateTimeFieldType.year(), pivot, lenientParse));
870 }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890 public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot) {
891 return appendTwoDigitWeekyear(pivot, false);
892 }
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908 public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot, boolean lenientParse) {
909 return append0(new TwoDigitYear(DateTimeFieldType.weekyear(), pivot, lenientParse));
910 }
911
912
913
914
915
916
917
918
919
920 public DateTimeFormatterBuilder appendYearOfEra(int minDigits, int maxDigits) {
921 return appendDecimal(DateTimeFieldType.yearOfEra(), minDigits, maxDigits);
922 }
923
924
925
926
927
928
929
930
931
932 public DateTimeFormatterBuilder appendYearOfCentury(int minDigits, int maxDigits) {
933 return appendDecimal(DateTimeFieldType.yearOfCentury(), minDigits, maxDigits);
934 }
935
936
937
938
939
940
941
942
943
944 public DateTimeFormatterBuilder appendCenturyOfEra(int minDigits, int maxDigits) {
945 return appendSignedDecimal(DateTimeFieldType.centuryOfEra(), minDigits, maxDigits);
946 }
947
948
949
950
951
952
953
954 public DateTimeFormatterBuilder appendHalfdayOfDayText() {
955 return appendText(DateTimeFieldType.halfdayOfDay());
956 }
957
958
959
960
961
962
963
964 public DateTimeFormatterBuilder appendDayOfWeekText() {
965 return appendText(DateTimeFieldType.dayOfWeek());
966 }
967
968
969
970
971
972
973
974
975 public DateTimeFormatterBuilder appendDayOfWeekShortText() {
976 return appendShortText(DateTimeFieldType.dayOfWeek());
977 }
978
979
980
981
982
983
984
985
986 public DateTimeFormatterBuilder appendMonthOfYearText() {
987 return appendText(DateTimeFieldType.monthOfYear());
988 }
989
990
991
992
993
994
995
996 public DateTimeFormatterBuilder appendMonthOfYearShortText() {
997 return appendShortText(DateTimeFieldType.monthOfYear());
998 }
999
1000
1001
1002
1003
1004
1005
1006 public DateTimeFormatterBuilder appendEraText() {
1007 return appendText(DateTimeFieldType.era());
1008 }
1009
1010
1011
1012
1013
1014
1015
1016
1017 public DateTimeFormatterBuilder appendTimeZoneName() {
1018 return append0(new TimeZoneName(TimeZoneName.LONG_NAME, null), null);
1019 }
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030 public DateTimeFormatterBuilder appendTimeZoneName(Map<String, DateTimeZone> parseLookup) {
1031 TimeZoneName pp = new TimeZoneName(TimeZoneName.LONG_NAME, parseLookup);
1032 return append0(pp, pp);
1033 }
1034
1035
1036
1037
1038
1039
1040
1041
1042 public DateTimeFormatterBuilder appendTimeZoneShortName() {
1043 return append0(new TimeZoneName(TimeZoneName.SHORT_NAME, null), null);
1044 }
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056 public DateTimeFormatterBuilder appendTimeZoneShortName(Map<String, DateTimeZone> parseLookup) {
1057 TimeZoneName pp = new TimeZoneName(TimeZoneName.SHORT_NAME, parseLookup);
1058 return append0(pp, pp);
1059 }
1060
1061
1062
1063
1064
1065
1066
1067 public DateTimeFormatterBuilder appendTimeZoneId() {
1068 return append0(TimeZoneId.INSTANCE, TimeZoneId.INSTANCE);
1069 }
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088 public DateTimeFormatterBuilder appendTimeZoneOffset(
1089 String zeroOffsetText, boolean showSeparators,
1090 int minFields, int maxFields) {
1091 return append0(new TimeZoneOffset
1092 (zeroOffsetText, zeroOffsetText, showSeparators, minFields, maxFields));
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115 public DateTimeFormatterBuilder appendTimeZoneOffset(
1116 String zeroOffsetPrintText, String zeroOffsetParseText, boolean showSeparators,
1117 int minFields, int maxFields) {
1118 return append0(new TimeZoneOffset
1119 (zeroOffsetPrintText, zeroOffsetParseText, showSeparators, minFields, maxFields));
1120 }
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131 public DateTimeFormatterBuilder appendPattern(String pattern) {
1132 DateTimeFormat.appendPatternTo(this, pattern);
1133 return this;
1134 }
1135
1136
1137 private Object getFormatter() {
1138 Object f = iFormatter;
1139
1140 if (f == null) {
1141 if (iElementPairs.size() == 2) {
1142 Object printer = iElementPairs.get(0);
1143 Object parser = iElementPairs.get(1);
1144
1145 if (printer != null) {
1146 if (printer == parser || parser == null) {
1147 f = printer;
1148 }
1149 } else {
1150 f = parser;
1151 }
1152 }
1153
1154 if (f == null) {
1155 f = new Composite(iElementPairs);
1156 }
1157
1158 iFormatter = f;
1159 }
1160
1161 return f;
1162 }
1163
1164 private boolean isPrinter(Object f) {
1165 if (f instanceof DateTimePrinter) {
1166 if (f instanceof Composite) {
1167 return ((Composite)f).isPrinter();
1168 }
1169 return true;
1170 }
1171 return false;
1172 }
1173
1174 private boolean isParser(Object f) {
1175 if (f instanceof DateTimeParser) {
1176 if (f instanceof Composite) {
1177 return ((Composite)f).isParser();
1178 }
1179 return true;
1180 }
1181 return false;
1182 }
1183
1184 private boolean isFormatter(Object f) {
1185 return (isPrinter(f) || isParser(f));
1186 }
1187
1188 static void appendUnknownString(StringBuffer buf, int len) {
1189 for (int i = len; --i >= 0;) {
1190 buf.append('\ufffd');
1191 }
1192 }
1193
1194 static void printUnknownString(Writer out, int len) throws IOException {
1195 for (int i = len; --i >= 0;) {
1196 out.write('\ufffd');
1197 }
1198 }
1199
1200
1201 static class CharacterLiteral
1202 implements DateTimePrinter, DateTimeParser {
1203
1204 private final char iValue;
1205
1206 CharacterLiteral(char value) {
1207 super();
1208 iValue = value;
1209 }
1210
1211 public int estimatePrintedLength() {
1212 return 1;
1213 }
1214
1215 public void printTo(
1216 StringBuffer buf, long instant, Chronology chrono,
1217 int displayOffset, DateTimeZone displayZone, Locale locale) {
1218 buf.append(iValue);
1219 }
1220
1221 public void printTo(
1222 Writer out, long instant, Chronology chrono,
1223 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1224 out.write(iValue);
1225 }
1226
1227 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1228 buf.append(iValue);
1229 }
1230
1231 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1232 out.write(iValue);
1233 }
1234
1235 public int estimateParsedLength() {
1236 return 1;
1237 }
1238
1239 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1240 if (position >= text.length()) {
1241 return ~position;
1242 }
1243
1244 char a = text.charAt(position);
1245 char b = iValue;
1246
1247 if (a != b) {
1248 a = Character.toUpperCase(a);
1249 b = Character.toUpperCase(b);
1250 if (a != b) {
1251 a = Character.toLowerCase(a);
1252 b = Character.toLowerCase(b);
1253 if (a != b) {
1254 return ~position;
1255 }
1256 }
1257 }
1258
1259 return position + 1;
1260 }
1261 }
1262
1263
1264 static class StringLiteral
1265 implements DateTimePrinter, DateTimeParser {
1266
1267 private final String iValue;
1268
1269 StringLiteral(String value) {
1270 super();
1271 iValue = value;
1272 }
1273
1274 public int estimatePrintedLength() {
1275 return iValue.length();
1276 }
1277
1278 public void printTo(
1279 StringBuffer buf, long instant, Chronology chrono,
1280 int displayOffset, DateTimeZone displayZone, Locale locale) {
1281 buf.append(iValue);
1282 }
1283
1284 public void printTo(
1285 Writer out, long instant, Chronology chrono,
1286 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1287 out.write(iValue);
1288 }
1289
1290 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1291 buf.append(iValue);
1292 }
1293
1294 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1295 out.write(iValue);
1296 }
1297
1298 public int estimateParsedLength() {
1299 return iValue.length();
1300 }
1301
1302 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1303 if (text.regionMatches(true, position, iValue, 0, iValue.length())) {
1304 return position + iValue.length();
1305 }
1306 return ~position;
1307 }
1308 }
1309
1310
1311 static abstract class NumberFormatter
1312 implements DateTimePrinter, DateTimeParser {
1313 protected final DateTimeFieldType iFieldType;
1314 protected final int iMaxParsedDigits;
1315 protected final boolean iSigned;
1316
1317 NumberFormatter(DateTimeFieldType fieldType,
1318 int maxParsedDigits, boolean signed) {
1319 super();
1320 iFieldType = fieldType;
1321 iMaxParsedDigits = maxParsedDigits;
1322 iSigned = signed;
1323 }
1324
1325 public int estimateParsedLength() {
1326 return iMaxParsedDigits;
1327 }
1328
1329 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1330 int limit = Math.min(iMaxParsedDigits, text.length() - position);
1331
1332 boolean negative = false;
1333 int length = 0;
1334 while (length < limit) {
1335 char c = text.charAt(position + length);
1336 if (length == 0 && (c == '-' || c == '+') && iSigned) {
1337 negative = c == '-';
1338
1339
1340 if (length + 1 >= limit ||
1341 (c = text.charAt(position + length + 1)) < '0' || c > '9')
1342 {
1343 break;
1344 }
1345
1346 if (negative) {
1347 length++;
1348 } else {
1349
1350 position++;
1351 }
1352
1353 limit = Math.min(limit + 1, text.length() - position);
1354 continue;
1355 }
1356 if (c < '0' || c > '9') {
1357 break;
1358 }
1359 length++;
1360 }
1361
1362 if (length == 0) {
1363 return ~position;
1364 }
1365
1366 int value;
1367 if (length >= 9) {
1368
1369
1370 value = Integer.parseInt(text.substring(position, position += length));
1371 } else {
1372 int i = position;
1373 if (negative) {
1374 i++;
1375 }
1376 try {
1377 value = text.charAt(i++) - '0';
1378 } catch (StringIndexOutOfBoundsException e) {
1379 return ~position;
1380 }
1381 position += length;
1382 while (i < position) {
1383 value = ((value << 3) + (value << 1)) + text.charAt(i++) - '0';
1384 }
1385 if (negative) {
1386 value = -value;
1387 }
1388 }
1389
1390 bucket.saveField(iFieldType, value);
1391 return position;
1392 }
1393 }
1394
1395
1396 static class UnpaddedNumber extends NumberFormatter {
1397
1398 protected UnpaddedNumber(DateTimeFieldType fieldType,
1399 int maxParsedDigits, boolean signed)
1400 {
1401 super(fieldType, maxParsedDigits, signed);
1402 }
1403
1404 public int estimatePrintedLength() {
1405 return iMaxParsedDigits;
1406 }
1407
1408 public void printTo(
1409 StringBuffer buf, long instant, Chronology chrono,
1410 int displayOffset, DateTimeZone displayZone, Locale locale) {
1411 try {
1412 DateTimeField field = iFieldType.getField(chrono);
1413 FormatUtils.appendUnpaddedInteger(buf, field.get(instant));
1414 } catch (RuntimeException e) {
1415 buf.append('\ufffd');
1416 }
1417 }
1418
1419 public void printTo(
1420 Writer out, long instant, Chronology chrono,
1421 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1422 try {
1423 DateTimeField field = iFieldType.getField(chrono);
1424 FormatUtils.writeUnpaddedInteger(out, field.get(instant));
1425 } catch (RuntimeException e) {
1426 out.write('\ufffd');
1427 }
1428 }
1429
1430 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1431 if (partial.isSupported(iFieldType)) {
1432 try {
1433 FormatUtils.appendUnpaddedInteger(buf, partial.get(iFieldType));
1434 } catch (RuntimeException e) {
1435 buf.append('\ufffd');
1436 }
1437 } else {
1438 buf.append('\ufffd');
1439 }
1440 }
1441
1442 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1443 if (partial.isSupported(iFieldType)) {
1444 try {
1445 FormatUtils.writeUnpaddedInteger(out, partial.get(iFieldType));
1446 } catch (RuntimeException e) {
1447 out.write('\ufffd');
1448 }
1449 } else {
1450 out.write('\ufffd');
1451 }
1452 }
1453 }
1454
1455
1456 static class PaddedNumber extends NumberFormatter {
1457
1458 protected final int iMinPrintedDigits;
1459
1460 protected PaddedNumber(DateTimeFieldType fieldType, int maxParsedDigits,
1461 boolean signed, int minPrintedDigits)
1462 {
1463 super(fieldType, maxParsedDigits, signed);
1464 iMinPrintedDigits = minPrintedDigits;
1465 }
1466
1467 public int estimatePrintedLength() {
1468 return iMaxParsedDigits;
1469 }
1470
1471 public void printTo(
1472 StringBuffer buf, long instant, Chronology chrono,
1473 int displayOffset, DateTimeZone displayZone, Locale locale) {
1474 try {
1475 DateTimeField field = iFieldType.getField(chrono);
1476 FormatUtils.appendPaddedInteger(buf, field.get(instant), iMinPrintedDigits);
1477 } catch (RuntimeException e) {
1478 appendUnknownString(buf, iMinPrintedDigits);
1479 }
1480 }
1481
1482 public void printTo(
1483 Writer out, long instant, Chronology chrono,
1484 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1485 try {
1486 DateTimeField field = iFieldType.getField(chrono);
1487 FormatUtils.writePaddedInteger(out, field.get(instant), iMinPrintedDigits);
1488 } catch (RuntimeException e) {
1489 printUnknownString(out, iMinPrintedDigits);
1490 }
1491 }
1492
1493 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1494 if (partial.isSupported(iFieldType)) {
1495 try {
1496 FormatUtils.appendPaddedInteger(buf, partial.get(iFieldType), iMinPrintedDigits);
1497 } catch (RuntimeException e) {
1498 appendUnknownString(buf, iMinPrintedDigits);
1499 }
1500 } else {
1501 appendUnknownString(buf, iMinPrintedDigits);
1502 }
1503 }
1504
1505 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1506 if (partial.isSupported(iFieldType)) {
1507 try {
1508 FormatUtils.writePaddedInteger(out, partial.get(iFieldType), iMinPrintedDigits);
1509 } catch (RuntimeException e) {
1510 printUnknownString(out, iMinPrintedDigits);
1511 }
1512 } else {
1513 printUnknownString(out, iMinPrintedDigits);
1514 }
1515 }
1516 }
1517
1518
1519 static class FixedNumber extends PaddedNumber {
1520
1521 protected FixedNumber(DateTimeFieldType fieldType, int numDigits, boolean signed) {
1522 super(fieldType, numDigits, signed, numDigits);
1523 }
1524
1525 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1526 int newPos = super.parseInto(bucket, text, position);
1527 if (newPos < 0) {
1528 return newPos;
1529 }
1530 int expectedPos = position + iMaxParsedDigits;
1531 if (newPos != expectedPos) {
1532 if (iSigned) {
1533 char c = text.charAt(position);
1534 if (c == '-' || c == '+') {
1535 expectedPos++;
1536 }
1537 }
1538 if (newPos > expectedPos) {
1539
1540 return ~(expectedPos + 1);
1541 } else if (newPos < expectedPos) {
1542
1543 return ~newPos;
1544 }
1545 }
1546 return newPos;
1547 }
1548 }
1549
1550
1551 static class TwoDigitYear
1552 implements DateTimePrinter, DateTimeParser {
1553
1554
1555 private final DateTimeFieldType iType;
1556
1557 private final int iPivot;
1558 private final boolean iLenientParse;
1559
1560 TwoDigitYear(DateTimeFieldType type, int pivot, boolean lenientParse) {
1561 super();
1562 iType = type;
1563 iPivot = pivot;
1564 iLenientParse = lenientParse;
1565 }
1566
1567 public int estimateParsedLength() {
1568 return iLenientParse ? 4 : 2;
1569 }
1570
1571 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1572 int limit = text.length() - position;
1573
1574 if (!iLenientParse) {
1575 limit = Math.min(2, limit);
1576 if (limit < 2) {
1577 return ~position;
1578 }
1579 } else {
1580 boolean hasSignChar = false;
1581 boolean negative = false;
1582 int length = 0;
1583 while (length < limit) {
1584 char c = text.charAt(position + length);
1585 if (length == 0 && (c == '-' || c == '+')) {
1586 hasSignChar = true;
1587 negative = c == '-';
1588 if (negative) {
1589 length++;
1590 } else {
1591
1592 position++;
1593 limit--;
1594 }
1595 continue;
1596 }
1597 if (c < '0' || c > '9') {
1598 break;
1599 }
1600 length++;
1601 }
1602
1603 if (length == 0) {
1604 return ~position;
1605 }
1606
1607 if (hasSignChar || length != 2) {
1608 int value;
1609 if (length >= 9) {
1610
1611
1612 value = Integer.parseInt(text.substring(position, position += length));
1613 } else {
1614 int i = position;
1615 if (negative) {
1616 i++;
1617 }
1618 try {
1619 value = text.charAt(i++) - '0';
1620 } catch (StringIndexOutOfBoundsException e) {
1621 return ~position;
1622 }
1623 position += length;
1624 while (i < position) {
1625 value = ((value << 3) + (value << 1)) + text.charAt(i++) - '0';
1626 }
1627 if (negative) {
1628 value = -value;
1629 }
1630 }
1631
1632 bucket.saveField(iType, value);
1633 return position;
1634 }
1635 }
1636
1637 int year;
1638 char c = text.charAt(position);
1639 if (c < '0' || c > '9') {
1640 return ~position;
1641 }
1642 year = c - '0';
1643 c = text.charAt(position + 1);
1644 if (c < '0' || c > '9') {
1645 return ~position;
1646 }
1647 year = ((year << 3) + (year << 1)) + c - '0';
1648
1649 int pivot = iPivot;
1650
1651 if (bucket.getPivotYear() != null) {
1652 pivot = bucket.getPivotYear().intValue();
1653 }
1654
1655 int low = pivot - 50;
1656
1657 int t;
1658 if (low >= 0) {
1659 t = low % 100;
1660 } else {
1661 t = 99 + ((low + 1) % 100);
1662 }
1663
1664 year += low + ((year < t) ? 100 : 0) - t;
1665
1666 bucket.saveField(iType, year);
1667 return position + 2;
1668 }
1669
1670 public int estimatePrintedLength() {
1671 return 2;
1672 }
1673
1674 public void printTo(
1675 StringBuffer buf, long instant, Chronology chrono,
1676 int displayOffset, DateTimeZone displayZone, Locale locale) {
1677 int year = getTwoDigitYear(instant, chrono);
1678 if (year < 0) {
1679 buf.append('\ufffd');
1680 buf.append('\ufffd');
1681 } else {
1682 FormatUtils.appendPaddedInteger(buf, year, 2);
1683 }
1684 }
1685
1686 public void printTo(
1687 Writer out, long instant, Chronology chrono,
1688 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1689 int year = getTwoDigitYear(instant, chrono);
1690 if (year < 0) {
1691 out.write('\ufffd');
1692 out.write('\ufffd');
1693 } else {
1694 FormatUtils.writePaddedInteger(out, year, 2);
1695 }
1696 }
1697
1698 private int getTwoDigitYear(long instant, Chronology chrono) {
1699 try {
1700 int year = iType.getField(chrono).get(instant);
1701 if (year < 0) {
1702 year = -year;
1703 }
1704 return year % 100;
1705 } catch (RuntimeException e) {
1706 return -1;
1707 }
1708 }
1709
1710 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1711 int year = getTwoDigitYear(partial);
1712 if (year < 0) {
1713 buf.append('\ufffd');
1714 buf.append('\ufffd');
1715 } else {
1716 FormatUtils.appendPaddedInteger(buf, year, 2);
1717 }
1718 }
1719
1720 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1721 int year = getTwoDigitYear(partial);
1722 if (year < 0) {
1723 out.write('\ufffd');
1724 out.write('\ufffd');
1725 } else {
1726 FormatUtils.writePaddedInteger(out, year, 2);
1727 }
1728 }
1729
1730 private int getTwoDigitYear(ReadablePartial partial) {
1731 if (partial.isSupported(iType)) {
1732 try {
1733 int year = partial.get(iType);
1734 if (year < 0) {
1735 year = -year;
1736 }
1737 return year % 100;
1738 } catch (RuntimeException e) {}
1739 }
1740 return -1;
1741 }
1742 }
1743
1744
1745 static class TextField
1746 implements DateTimePrinter, DateTimeParser {
1747
1748 private static Map<Locale, Map<DateTimeFieldType, Object[]>> cParseCache =
1749 new HashMap<Locale, Map<DateTimeFieldType, Object[]>>();
1750 private final DateTimeFieldType iFieldType;
1751 private final boolean iShort;
1752
1753 TextField(DateTimeFieldType fieldType, boolean isShort) {
1754 super();
1755 iFieldType = fieldType;
1756 iShort = isShort;
1757 }
1758
1759 public int estimatePrintedLength() {
1760 return iShort ? 6 : 20;
1761 }
1762
1763 public void printTo(
1764 StringBuffer buf, long instant, Chronology chrono,
1765 int displayOffset, DateTimeZone displayZone, Locale locale) {
1766 try {
1767 buf.append(print(instant, chrono, locale));
1768 } catch (RuntimeException e) {
1769 buf.append('\ufffd');
1770 }
1771 }
1772
1773 public void printTo(
1774 Writer out, long instant, Chronology chrono,
1775 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1776 try {
1777 out.write(print(instant, chrono, locale));
1778 } catch (RuntimeException e) {
1779 out.write('\ufffd');
1780 }
1781 }
1782
1783 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1784 try {
1785 buf.append(print(partial, locale));
1786 } catch (RuntimeException e) {
1787 buf.append('\ufffd');
1788 }
1789 }
1790
1791 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1792 try {
1793 out.write(print(partial, locale));
1794 } catch (RuntimeException e) {
1795 out.write('\ufffd');
1796 }
1797 }
1798
1799 private String print(long instant, Chronology chrono, Locale locale) {
1800 DateTimeField field = iFieldType.getField(chrono);
1801 if (iShort) {
1802 return field.getAsShortText(instant, locale);
1803 } else {
1804 return field.getAsText(instant, locale);
1805 }
1806 }
1807
1808 private String print(ReadablePartial partial, Locale locale) {
1809 if (partial.isSupported(iFieldType)) {
1810 DateTimeField field = iFieldType.getField(partial.getChronology());
1811 if (iShort) {
1812 return field.getAsShortText(partial, locale);
1813 } else {
1814 return field.getAsText(partial, locale);
1815 }
1816 } else {
1817 return "\ufffd";
1818 }
1819 }
1820
1821 public int estimateParsedLength() {
1822 return estimatePrintedLength();
1823 }
1824
1825 @SuppressWarnings("unchecked")
1826 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
1827 Locale locale = bucket.getLocale();
1828
1829
1830 Set<String> validValues = null;
1831 int maxLength = 0;
1832 synchronized (cParseCache) {
1833 Map<DateTimeFieldType, Object[]> innerMap = cParseCache.get(locale);
1834 if (innerMap == null) {
1835 innerMap = new HashMap<DateTimeFieldType, Object[]>();
1836 cParseCache.put(locale, innerMap);
1837 }
1838 Object[] array = innerMap.get(iFieldType);
1839 if (array == null) {
1840 validValues = new HashSet<String>(32);
1841 MutableDateTime dt = new MutableDateTime(0L, DateTimeZone.UTC);
1842 Property property = dt.property(iFieldType);
1843 int min = property.getMinimumValueOverall();
1844 int max = property.getMaximumValueOverall();
1845 if (max - min > 32) {
1846 return ~position;
1847 }
1848 maxLength = property.getMaximumTextLength(locale);
1849 for (int i = min; i <= max; i++) {
1850 property.set(i);
1851 validValues.add(property.getAsShortText(locale));
1852 validValues.add(property.getAsShortText(locale).toLowerCase(locale));
1853 validValues.add(property.getAsShortText(locale).toUpperCase(locale));
1854 validValues.add(property.getAsText(locale));
1855 validValues.add(property.getAsText(locale).toLowerCase(locale));
1856 validValues.add(property.getAsText(locale).toUpperCase(locale));
1857 }
1858 if ("en".equals(locale.getLanguage()) && iFieldType == DateTimeFieldType.era()) {
1859
1860 validValues.add("BCE");
1861 validValues.add("bce");
1862 validValues.add("CE");
1863 validValues.add("ce");
1864 maxLength = 3;
1865 }
1866 array = new Object[] {validValues, Integer.valueOf(maxLength)};
1867 innerMap.put(iFieldType, array);
1868 } else {
1869 validValues = (Set<String>) array[0];
1870 maxLength = ((Integer) array[1]).intValue();
1871 }
1872 }
1873
1874 int limit = Math.min(text.length(), position + maxLength);
1875 for (int i = limit; i > position; i--) {
1876 String match = text.substring(position, i);
1877 if (validValues.contains(match)) {
1878 bucket.saveField(iFieldType, match, locale);
1879 return i;
1880 }
1881 }
1882 return ~position;
1883 }
1884 }
1885
1886
1887 static class Fraction
1888 implements DateTimePrinter, DateTimeParser {
1889
1890 private final DateTimeFieldType iFieldType;
1891 protected int iMinDigits;
1892 protected int iMaxDigits;
1893
1894 protected Fraction(DateTimeFieldType fieldType, int minDigits, int maxDigits) {
1895 super();
1896 iFieldType = fieldType;
1897
1898 if (maxDigits > 18) {
1899 maxDigits = 18;
1900 }
1901 iMinDigits = minDigits;
1902 iMaxDigits = maxDigits;
1903 }
1904
1905 public int estimatePrintedLength() {
1906 return iMaxDigits;
1907 }
1908
1909 public void printTo(
1910 StringBuffer buf, long instant, Chronology chrono,
1911 int displayOffset, DateTimeZone displayZone, Locale locale) {
1912 try {
1913 printTo(buf, null, instant, chrono);
1914 } catch (IOException e) {
1915
1916 }
1917 }
1918
1919 public void printTo(
1920 Writer out, long instant, Chronology chrono,
1921 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
1922 printTo(null, out, instant, chrono);
1923 }
1924
1925 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
1926
1927
1928 long millis = partial.getChronology().set(partial, 0L);
1929 try {
1930 printTo(buf, null, millis, partial.getChronology());
1931 } catch (IOException e) {
1932
1933 }
1934 }
1935
1936 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
1937
1938
1939 long millis = partial.getChronology().set(partial, 0L);
1940 printTo(null, out, millis, partial.getChronology());
1941 }
1942
1943 protected void printTo(StringBuffer buf, Writer out, long instant, Chronology chrono)
1944 throws IOException
1945 {
1946 DateTimeField field = iFieldType.getField(chrono);
1947 int minDigits = iMinDigits;
1948
1949 long fraction;
1950 try {
1951 fraction = field.remainder(instant);
1952 } catch (RuntimeException e) {
1953 if (buf != null) {
1954 appendUnknownString(buf, minDigits);
1955 } else {
1956 printUnknownString(out, minDigits);
1957 }
1958 return;
1959 }
1960
1961 if (fraction == 0) {
1962 if (buf != null) {
1963 while (--minDigits >= 0) {
1964 buf.append('0');
1965 }
1966 } else {
1967 while (--minDigits >= 0) {
1968 out.write('0');
1969 }
1970 }
1971 return;
1972 }
1973
1974 String str;
1975 long[] fractionData = getFractionData(fraction, field);
1976 long scaled = fractionData[0];
1977 int maxDigits = (int) fractionData[1];
1978
1979 if ((scaled & 0x7fffffff) == scaled) {
1980 str = Integer.toString((int) scaled);
1981 } else {
1982 str = Long.toString(scaled);
1983 }
1984
1985 int length = str.length();
1986 int digits = maxDigits;
1987 while (length < digits) {
1988 if (buf != null) {
1989 buf.append('0');
1990 } else {
1991 out.write('0');
1992 }
1993 minDigits--;
1994 digits--;
1995 }
1996
1997 if (minDigits < digits) {
1998
1999 while (minDigits < digits) {
2000 if (length <= 1 || str.charAt(length - 1) != '0') {
2001 break;
2002 }
2003 digits--;
2004 length--;
2005 }
2006 if (length < str.length()) {
2007 if (buf != null) {
2008 for (int i=0; i<length; i++) {
2009 buf.append(str.charAt(i));
2010 }
2011 } else {
2012 for (int i=0; i<length; i++) {
2013 out.write(str.charAt(i));
2014 }
2015 }
2016 return;
2017 }
2018 }
2019
2020 if (buf != null) {
2021 buf.append(str);
2022 } else {
2023 out.write(str);
2024 }
2025 }
2026
2027 private long[] getFractionData(long fraction, DateTimeField field) {
2028 long rangeMillis = field.getDurationField().getUnitMillis();
2029 long scalar;
2030 int maxDigits = iMaxDigits;
2031 while (true) {
2032 switch (maxDigits) {
2033 default: scalar = 1L; break;
2034 case 1: scalar = 10L; break;
2035 case 2: scalar = 100L; break;
2036 case 3: scalar = 1000L; break;
2037 case 4: scalar = 10000L; break;
2038 case 5: scalar = 100000L; break;
2039 case 6: scalar = 1000000L; break;
2040 case 7: scalar = 10000000L; break;
2041 case 8: scalar = 100000000L; break;
2042 case 9: scalar = 1000000000L; break;
2043 case 10: scalar = 10000000000L; break;
2044 case 11: scalar = 100000000000L; break;
2045 case 12: scalar = 1000000000000L; break;
2046 case 13: scalar = 10000000000000L; break;
2047 case 14: scalar = 100000000000000L; break;
2048 case 15: scalar = 1000000000000000L; break;
2049 case 16: scalar = 10000000000000000L; break;
2050 case 17: scalar = 100000000000000000L; break;
2051 case 18: scalar = 1000000000000000000L; break;
2052 }
2053 if (((rangeMillis * scalar) / scalar) == rangeMillis) {
2054 break;
2055 }
2056
2057 maxDigits--;
2058 }
2059
2060 return new long[] {fraction * scalar / rangeMillis, maxDigits};
2061 }
2062
2063 public int estimateParsedLength() {
2064 return iMaxDigits;
2065 }
2066
2067 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2068 DateTimeField field = iFieldType.getField(bucket.getChronology());
2069
2070 int limit = Math.min(iMaxDigits, text.length() - position);
2071
2072 long value = 0;
2073 long n = field.getDurationField().getUnitMillis() * 10;
2074 int length = 0;
2075 while (length < limit) {
2076 char c = text.charAt(position + length);
2077 if (c < '0' || c > '9') {
2078 break;
2079 }
2080 length++;
2081 long nn = n / 10;
2082 value += (c - '0') * nn;
2083 n = nn;
2084 }
2085
2086 value /= 10;
2087
2088 if (length == 0) {
2089 return ~position;
2090 }
2091
2092 if (value > Integer.MAX_VALUE) {
2093 return ~position;
2094 }
2095
2096 DateTimeField parseField = new PreciseDateTimeField(
2097 DateTimeFieldType.millisOfSecond(),
2098 MillisDurationField.INSTANCE,
2099 field.getDurationField());
2100
2101 bucket.saveField(parseField, (int) value);
2102
2103 return position + length;
2104 }
2105 }
2106
2107
2108 static class TimeZoneOffset
2109 implements DateTimePrinter, DateTimeParser {
2110
2111 private final String iZeroOffsetPrintText;
2112 private final String iZeroOffsetParseText;
2113 private final boolean iShowSeparators;
2114 private final int iMinFields;
2115 private final int iMaxFields;
2116
2117 TimeZoneOffset(String zeroOffsetPrintText, String zeroOffsetParseText,
2118 boolean showSeparators,
2119 int minFields, int maxFields)
2120 {
2121 super();
2122 iZeroOffsetPrintText = zeroOffsetPrintText;
2123 iZeroOffsetParseText = zeroOffsetParseText;
2124 iShowSeparators = showSeparators;
2125 if (minFields <= 0 || maxFields < minFields) {
2126 throw new IllegalArgumentException();
2127 }
2128 if (minFields > 4) {
2129 minFields = 4;
2130 maxFields = 4;
2131 }
2132 iMinFields = minFields;
2133 iMaxFields = maxFields;
2134 }
2135
2136 public int estimatePrintedLength() {
2137 int est = 1 + iMinFields << 1;
2138 if (iShowSeparators) {
2139 est += iMinFields - 1;
2140 }
2141 if (iZeroOffsetPrintText != null && iZeroOffsetPrintText.length() > est) {
2142 est = iZeroOffsetPrintText.length();
2143 }
2144 return est;
2145 }
2146
2147 public void printTo(
2148 StringBuffer buf, long instant, Chronology chrono,
2149 int displayOffset, DateTimeZone displayZone, Locale locale) {
2150 if (displayZone == null) {
2151 return;
2152 }
2153 if (displayOffset == 0 && iZeroOffsetPrintText != null) {
2154 buf.append(iZeroOffsetPrintText);
2155 return;
2156 }
2157 if (displayOffset >= 0) {
2158 buf.append('+');
2159 } else {
2160 buf.append('-');
2161 displayOffset = -displayOffset;
2162 }
2163
2164 int hours = displayOffset / DateTimeConstants.MILLIS_PER_HOUR;
2165 FormatUtils.appendPaddedInteger(buf, hours, 2);
2166 if (iMaxFields == 1) {
2167 return;
2168 }
2169 displayOffset -= hours * (int)DateTimeConstants.MILLIS_PER_HOUR;
2170 if (displayOffset == 0 && iMinFields <= 1) {
2171 return;
2172 }
2173
2174 int minutes = displayOffset / DateTimeConstants.MILLIS_PER_MINUTE;
2175 if (iShowSeparators) {
2176 buf.append(':');
2177 }
2178 FormatUtils.appendPaddedInteger(buf, minutes, 2);
2179 if (iMaxFields == 2) {
2180 return;
2181 }
2182 displayOffset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
2183 if (displayOffset == 0 && iMinFields <= 2) {
2184 return;
2185 }
2186
2187 int seconds = displayOffset / DateTimeConstants.MILLIS_PER_SECOND;
2188 if (iShowSeparators) {
2189 buf.append(':');
2190 }
2191 FormatUtils.appendPaddedInteger(buf, seconds, 2);
2192 if (iMaxFields == 3) {
2193 return;
2194 }
2195 displayOffset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
2196 if (displayOffset == 0 && iMinFields <= 3) {
2197 return;
2198 }
2199
2200 if (iShowSeparators) {
2201 buf.append('.');
2202 }
2203 FormatUtils.appendPaddedInteger(buf, displayOffset, 3);
2204 }
2205
2206 public void printTo(
2207 Writer out, long instant, Chronology chrono,
2208 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
2209 if (displayZone == null) {
2210 return;
2211 }
2212 if (displayOffset == 0 && iZeroOffsetPrintText != null) {
2213 out.write(iZeroOffsetPrintText);
2214 return;
2215 }
2216 if (displayOffset >= 0) {
2217 out.write('+');
2218 } else {
2219 out.write('-');
2220 displayOffset = -displayOffset;
2221 }
2222
2223 int hours = displayOffset / DateTimeConstants.MILLIS_PER_HOUR;
2224 FormatUtils.writePaddedInteger(out, hours, 2);
2225 if (iMaxFields == 1) {
2226 return;
2227 }
2228 displayOffset -= hours * (int)DateTimeConstants.MILLIS_PER_HOUR;
2229 if (displayOffset == 0 && iMinFields == 1) {
2230 return;
2231 }
2232
2233 int minutes = displayOffset / DateTimeConstants.MILLIS_PER_MINUTE;
2234 if (iShowSeparators) {
2235 out.write(':');
2236 }
2237 FormatUtils.writePaddedInteger(out, minutes, 2);
2238 if (iMaxFields == 2) {
2239 return;
2240 }
2241 displayOffset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
2242 if (displayOffset == 0 && iMinFields == 2) {
2243 return;
2244 }
2245
2246 int seconds = displayOffset / DateTimeConstants.MILLIS_PER_SECOND;
2247 if (iShowSeparators) {
2248 out.write(':');
2249 }
2250 FormatUtils.writePaddedInteger(out, seconds, 2);
2251 if (iMaxFields == 3) {
2252 return;
2253 }
2254 displayOffset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
2255 if (displayOffset == 0 && iMinFields == 3) {
2256 return;
2257 }
2258
2259 if (iShowSeparators) {
2260 out.write('.');
2261 }
2262 FormatUtils.writePaddedInteger(out, displayOffset, 3);
2263 }
2264
2265 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
2266
2267 }
2268
2269 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
2270
2271 }
2272
2273 public int estimateParsedLength() {
2274 return estimatePrintedLength();
2275 }
2276
2277 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2278 int limit = text.length() - position;
2279
2280 zeroOffset:
2281 if (iZeroOffsetParseText != null) {
2282 if (iZeroOffsetParseText.length() == 0) {
2283
2284 if (limit > 0) {
2285 char c = text.charAt(position);
2286 if (c == '-' || c == '+') {
2287 break zeroOffset;
2288 }
2289 }
2290 bucket.setOffset(Integer.valueOf(0));
2291 return position;
2292 }
2293 if (text.regionMatches(true, position, iZeroOffsetParseText, 0, iZeroOffsetParseText.length())) {
2294 bucket.setOffset(Integer.valueOf(0));
2295 return position + iZeroOffsetParseText.length();
2296 }
2297 }
2298
2299
2300
2301 if (limit <= 1) {
2302 return ~position;
2303 }
2304
2305 boolean negative;
2306 char c = text.charAt(position);
2307 if (c == '-') {
2308 negative = true;
2309 } else if (c == '+') {
2310 negative = false;
2311 } else {
2312 return ~position;
2313 }
2314
2315 limit--;
2316 position++;
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330 if (digitCount(text, position, 2) < 2) {
2331
2332 return ~position;
2333 }
2334
2335 int offset;
2336
2337 int hours = FormatUtils.parseTwoDigits(text, position);
2338 if (hours > 23) {
2339 return ~position;
2340 }
2341 offset = hours * DateTimeConstants.MILLIS_PER_HOUR;
2342 limit -= 2;
2343 position += 2;
2344
2345 parse: {
2346
2347
2348
2349 if (limit <= 0) {
2350 break parse;
2351 }
2352
2353 boolean expectSeparators;
2354 c = text.charAt(position);
2355 if (c == ':') {
2356 expectSeparators = true;
2357 limit--;
2358 position++;
2359 } else if (c >= '0' && c <= '9') {
2360 expectSeparators = false;
2361 } else {
2362 break parse;
2363 }
2364
2365
2366
2367 int count = digitCount(text, position, 2);
2368 if (count == 0 && !expectSeparators) {
2369 break parse;
2370 } else if (count < 2) {
2371
2372 return ~position;
2373 }
2374
2375 int minutes = FormatUtils.parseTwoDigits(text, position);
2376 if (minutes > 59) {
2377 return ~position;
2378 }
2379 offset += minutes * DateTimeConstants.MILLIS_PER_MINUTE;
2380 limit -= 2;
2381 position += 2;
2382
2383
2384
2385 if (limit <= 0) {
2386 break parse;
2387 }
2388
2389 if (expectSeparators) {
2390 if (text.charAt(position) != ':') {
2391 break parse;
2392 }
2393 limit--;
2394 position++;
2395 }
2396
2397 count = digitCount(text, position, 2);
2398 if (count == 0 && !expectSeparators) {
2399 break parse;
2400 } else if (count < 2) {
2401
2402 return ~position;
2403 }
2404
2405 int seconds = FormatUtils.parseTwoDigits(text, position);
2406 if (seconds > 59) {
2407 return ~position;
2408 }
2409 offset += seconds * DateTimeConstants.MILLIS_PER_SECOND;
2410 limit -= 2;
2411 position += 2;
2412
2413
2414
2415 if (limit <= 0) {
2416 break parse;
2417 }
2418
2419 if (expectSeparators) {
2420 if (text.charAt(position) != '.' && text.charAt(position) != ',') {
2421 break parse;
2422 }
2423 limit--;
2424 position++;
2425 }
2426
2427 count = digitCount(text, position, 3);
2428 if (count == 0 && !expectSeparators) {
2429 break parse;
2430 } else if (count < 1) {
2431
2432 return ~position;
2433 }
2434
2435 offset += (text.charAt(position++) - '0') * 100;
2436 if (count > 1) {
2437 offset += (text.charAt(position++) - '0') * 10;
2438 if (count > 2) {
2439 offset += text.charAt(position++) - '0';
2440 }
2441 }
2442 }
2443
2444 bucket.setOffset(Integer.valueOf(negative ? -offset : offset));
2445 return position;
2446 }
2447
2448
2449
2450
2451
2452 private int digitCount(String text, int position, int amount) {
2453 int limit = Math.min(text.length() - position, amount);
2454 amount = 0;
2455 for (; limit > 0; limit--) {
2456 char c = text.charAt(position + amount);
2457 if (c < '0' || c > '9') {
2458 break;
2459 }
2460 amount++;
2461 }
2462 return amount;
2463 }
2464 }
2465
2466
2467 static class TimeZoneName
2468 implements DateTimePrinter, DateTimeParser {
2469
2470 static final int LONG_NAME = 0;
2471 static final int SHORT_NAME = 1;
2472
2473 private final Map<String, DateTimeZone> iParseLookup;
2474 private final int iType;
2475
2476 TimeZoneName(int type, Map<String, DateTimeZone> parseLookup) {
2477 super();
2478 iType = type;
2479 iParseLookup = parseLookup;
2480 }
2481
2482 public int estimatePrintedLength() {
2483 return (iType == SHORT_NAME ? 4 : 20);
2484 }
2485
2486 public void printTo(
2487 StringBuffer buf, long instant, Chronology chrono,
2488 int displayOffset, DateTimeZone displayZone, Locale locale) {
2489 buf.append(print(instant - displayOffset, displayZone, locale));
2490 }
2491
2492 public void printTo(
2493 Writer out, long instant, Chronology chrono,
2494 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
2495 out.write(print(instant - displayOffset, displayZone, locale));
2496 }
2497
2498 private String print(long instant, DateTimeZone displayZone, Locale locale) {
2499 if (displayZone == null) {
2500 return "";
2501 }
2502 switch (iType) {
2503 case LONG_NAME:
2504 return displayZone.getName(instant, locale);
2505 case SHORT_NAME:
2506 return displayZone.getShortName(instant, locale);
2507 }
2508 return "";
2509 }
2510
2511 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
2512
2513 }
2514
2515 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
2516
2517 }
2518
2519 public int estimateParsedLength() {
2520 return (iType == SHORT_NAME ? 4 : 20);
2521 }
2522
2523 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2524 Map<String, DateTimeZone> parseLookup = iParseLookup;
2525 parseLookup = (parseLookup != null ? parseLookup : DateTimeUtils.getDefaultTimeZoneNames());
2526 String str = text.substring(position);
2527 for (String name : parseLookup.keySet()) {
2528 if (str.startsWith(name)) {
2529 bucket.setZone(parseLookup.get(name));
2530 return position + name.length();
2531 }
2532 }
2533 return ~position;
2534 }
2535 }
2536
2537
2538 static enum TimeZoneId
2539 implements DateTimePrinter, DateTimeParser {
2540
2541 INSTANCE;
2542 static final Set<String> ALL_IDS = DateTimeZone.getAvailableIDs();
2543 static final int MAX_LENGTH;
2544 static {
2545 int max = 0;
2546 for (String id : ALL_IDS) {
2547 max = Math.max(max, id.length());
2548 }
2549 MAX_LENGTH = max;
2550 }
2551
2552 public int estimatePrintedLength() {
2553 return MAX_LENGTH;
2554 }
2555
2556 public void printTo(
2557 StringBuffer buf, long instant, Chronology chrono,
2558 int displayOffset, DateTimeZone displayZone, Locale locale) {
2559 buf.append(displayZone != null ? displayZone.getID() : "");
2560 }
2561
2562 public void printTo(
2563 Writer out, long instant, Chronology chrono,
2564 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
2565 out.write(displayZone != null ? displayZone.getID() : "");
2566 }
2567
2568 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
2569
2570 }
2571
2572 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
2573
2574 }
2575
2576 public int estimateParsedLength() {
2577 return MAX_LENGTH;
2578 }
2579
2580 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2581 String str = text.substring(position);
2582 String best = null;
2583 for (String id : ALL_IDS) {
2584 if (str.startsWith(id)) {
2585 if (best == null || id.length() > best.length()) {
2586 best = id;
2587 }
2588 }
2589 }
2590 if (best != null) {
2591 bucket.setZone(DateTimeZone.forID(best));
2592 return position + best.length();
2593 }
2594 return ~position;
2595 }
2596 }
2597
2598
2599 static class Composite
2600 implements DateTimePrinter, DateTimeParser {
2601
2602 private final DateTimePrinter[] iPrinters;
2603 private final DateTimeParser[] iParsers;
2604
2605 private final int iPrintedLengthEstimate;
2606 private final int iParsedLengthEstimate;
2607
2608 Composite(List<Object> elementPairs) {
2609 super();
2610
2611 List<Object> printerList = new ArrayList<Object>();
2612 List<Object> parserList = new ArrayList<Object>();
2613
2614 decompose(elementPairs, printerList, parserList);
2615
2616 if (printerList.contains(null) || printerList.isEmpty()) {
2617 iPrinters = null;
2618 iPrintedLengthEstimate = 0;
2619 } else {
2620 int size = printerList.size();
2621 iPrinters = new DateTimePrinter[size];
2622 int printEst = 0;
2623 for (int i=0; i<size; i++) {
2624 DateTimePrinter printer = (DateTimePrinter) printerList.get(i);
2625 printEst += printer.estimatePrintedLength();
2626 iPrinters[i] = printer;
2627 }
2628 iPrintedLengthEstimate = printEst;
2629 }
2630
2631 if (parserList.contains(null) || parserList.isEmpty()) {
2632 iParsers = null;
2633 iParsedLengthEstimate = 0;
2634 } else {
2635 int size = parserList.size();
2636 iParsers = new DateTimeParser[size];
2637 int parseEst = 0;
2638 for (int i=0; i<size; i++) {
2639 DateTimeParser parser = (DateTimeParser) parserList.get(i);
2640 parseEst += parser.estimateParsedLength();
2641 iParsers[i] = parser;
2642 }
2643 iParsedLengthEstimate = parseEst;
2644 }
2645 }
2646
2647 public int estimatePrintedLength() {
2648 return iPrintedLengthEstimate;
2649 }
2650
2651 public void printTo(
2652 StringBuffer buf, long instant, Chronology chrono,
2653 int displayOffset, DateTimeZone displayZone, Locale locale) {
2654 DateTimePrinter[] elements = iPrinters;
2655 if (elements == null) {
2656 throw new UnsupportedOperationException();
2657 }
2658
2659 if (locale == null) {
2660
2661 locale = Locale.getDefault();
2662 }
2663
2664 int len = elements.length;
2665 for (int i = 0; i < len; i++) {
2666 elements[i].printTo(buf, instant, chrono, displayOffset, displayZone, locale);
2667 }
2668 }
2669
2670 public void printTo(
2671 Writer out, long instant, Chronology chrono,
2672 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
2673 DateTimePrinter[] elements = iPrinters;
2674 if (elements == null) {
2675 throw new UnsupportedOperationException();
2676 }
2677
2678 if (locale == null) {
2679
2680 locale = Locale.getDefault();
2681 }
2682
2683 int len = elements.length;
2684 for (int i = 0; i < len; i++) {
2685 elements[i].printTo(out, instant, chrono, displayOffset, displayZone, locale);
2686 }
2687 }
2688
2689 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
2690 DateTimePrinter[] elements = iPrinters;
2691 if (elements == null) {
2692 throw new UnsupportedOperationException();
2693 }
2694
2695 if (locale == null) {
2696
2697 locale = Locale.getDefault();
2698 }
2699
2700 int len = elements.length;
2701 for (int i=0; i<len; i++) {
2702 elements[i].printTo(buf, partial, locale);
2703 }
2704 }
2705
2706 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException {
2707 DateTimePrinter[] elements = iPrinters;
2708 if (elements == null) {
2709 throw new UnsupportedOperationException();
2710 }
2711
2712 if (locale == null) {
2713
2714 locale = Locale.getDefault();
2715 }
2716
2717 int len = elements.length;
2718 for (int i=0; i<len; i++) {
2719 elements[i].printTo(out, partial, locale);
2720 }
2721 }
2722
2723 public int estimateParsedLength() {
2724 return iParsedLengthEstimate;
2725 }
2726
2727 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2728 DateTimeParser[] elements = iParsers;
2729 if (elements == null) {
2730 throw new UnsupportedOperationException();
2731 }
2732
2733 int len = elements.length;
2734 for (int i=0; i<len && position >= 0; i++) {
2735 position = elements[i].parseInto(bucket, text, position);
2736 }
2737 return position;
2738 }
2739
2740 boolean isPrinter() {
2741 return iPrinters != null;
2742 }
2743
2744 boolean isParser() {
2745 return iParsers != null;
2746 }
2747
2748
2749
2750
2751
2752 private void decompose(List<Object> elementPairs, List<Object> printerList, List<Object> parserList) {
2753 int size = elementPairs.size();
2754 for (int i=0; i<size; i+=2) {
2755 Object element = elementPairs.get(i);
2756 if (element instanceof Composite) {
2757 addArrayToList(printerList, ((Composite)element).iPrinters);
2758 } else {
2759 printerList.add(element);
2760 }
2761
2762 element = elementPairs.get(i + 1);
2763 if (element instanceof Composite) {
2764 addArrayToList(parserList, ((Composite)element).iParsers);
2765 } else {
2766 parserList.add(element);
2767 }
2768 }
2769 }
2770
2771 private void addArrayToList(List<Object> list, Object[] array) {
2772 if (array != null) {
2773 for (int i=0; i<array.length; i++) {
2774 list.add(array[i]);
2775 }
2776 }
2777 }
2778 }
2779
2780
2781 static class MatchingParser
2782 implements DateTimeParser {
2783
2784 private final DateTimeParser[] iParsers;
2785 private final int iParsedLengthEstimate;
2786
2787 MatchingParser(DateTimeParser[] parsers) {
2788 super();
2789 iParsers = parsers;
2790 int est = 0;
2791 for (int i=parsers.length; --i>=0 ;) {
2792 DateTimeParser parser = parsers[i];
2793 if (parser != null) {
2794 int len = parser.estimateParsedLength();
2795 if (len > est) {
2796 est = len;
2797 }
2798 }
2799 }
2800 iParsedLengthEstimate = est;
2801 }
2802
2803 public int estimateParsedLength() {
2804 return iParsedLengthEstimate;
2805 }
2806
2807 public int parseInto(DateTimeParserBucket bucket, String text, int position) {
2808 DateTimeParser[] parsers = iParsers;
2809 int length = parsers.length;
2810
2811 final Object originalState = bucket.saveState();
2812 boolean isOptional = false;
2813
2814 int bestValidPos = position;
2815 Object bestValidState = null;
2816
2817 int bestInvalidPos = position;
2818
2819 for (int i=0; i<length; i++) {
2820 DateTimeParser parser = parsers[i];
2821 if (parser == null) {
2822
2823 if (bestValidPos <= position) {
2824 return position;
2825 }
2826 isOptional = true;
2827 break;
2828 }
2829 int parsePos = parser.parseInto(bucket, text, position);
2830 if (parsePos >= position) {
2831 if (parsePos > bestValidPos) {
2832 if (parsePos >= text.length() ||
2833 (i + 1) >= length || parsers[i + 1] == null) {
2834
2835
2836
2837 return parsePos;
2838 }
2839 bestValidPos = parsePos;
2840 bestValidState = bucket.saveState();
2841 }
2842 } else {
2843 if (parsePos < 0) {
2844 parsePos = ~parsePos;
2845 if (parsePos > bestInvalidPos) {
2846 bestInvalidPos = parsePos;
2847 }
2848 }
2849 }
2850 bucket.restoreState(originalState);
2851 }
2852
2853 if (bestValidPos > position || (bestValidPos == position && isOptional)) {
2854
2855 if (bestValidState != null) {
2856 bucket.restoreState(bestValidState);
2857 }
2858 return bestValidPos;
2859 }
2860
2861 return ~bestInvalidPos;
2862 }
2863 }
2864
2865 }