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.Collections;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.TreeSet;
25
26 import org.joda.time.DateTimeConstants;
27 import org.joda.time.DurationFieldType;
28 import org.joda.time.PeriodType;
29 import org.joda.time.ReadWritablePeriod;
30 import org.joda.time.ReadablePeriod;
31
32
33
34
35
36
37
38
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 public class PeriodFormatterBuilder {
66 private static final int PRINT_ZERO_RARELY_FIRST = 1;
67 private static final int PRINT_ZERO_RARELY_LAST = 2;
68 private static final int PRINT_ZERO_IF_SUPPORTED = 3;
69 private static final int PRINT_ZERO_ALWAYS = 4;
70 private static final int PRINT_ZERO_NEVER = 5;
71
72 private static final int YEARS = 0;
73 private static final int MONTHS = 1;
74 private static final int WEEKS = 2;
75 private static final int DAYS = 3;
76 private static final int HOURS = 4;
77 private static final int MINUTES = 5;
78 private static final int SECONDS = 6;
79 private static final int MILLIS = 7;
80 private static final int SECONDS_MILLIS = 8;
81 private static final int SECONDS_OPTIONAL_MILLIS = 9;
82 private static final int MAX_FIELD = SECONDS_OPTIONAL_MILLIS;
83
84 private int iMinPrintedDigits;
85 private int iPrintZeroSetting;
86 private int iMaxParsedDigits;
87 private boolean iRejectSignedValues;
88
89 private PeriodFieldAffix iPrefix;
90
91
92 private List<Object> iElementPairs;
93
94 private boolean iNotPrinter;
95
96 private boolean iNotParser;
97
98
99 private FieldFormatter[] iFieldFormatters;
100
101 public PeriodFormatterBuilder() {
102 clear();
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public PeriodFormatter toFormatter() {
123 PeriodFormatter formatter = toFormatter(iElementPairs, iNotPrinter, iNotParser);
124 iFieldFormatters = (FieldFormatter[]) iFieldFormatters.clone();
125 return formatter;
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public PeriodPrinter toPrinter() {
141 if (iNotPrinter) {
142 return null;
143 }
144 return toFormatter().getPrinter();
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public PeriodParser toParser() {
160 if (iNotParser) {
161 return null;
162 }
163 return toFormatter().getParser();
164 }
165
166
167
168
169
170 public void clear() {
171 iMinPrintedDigits = 1;
172 iPrintZeroSetting = PRINT_ZERO_RARELY_LAST;
173 iMaxParsedDigits = 10;
174 iRejectSignedValues = false;
175 iPrefix = null;
176 if (iElementPairs == null) {
177 iElementPairs = new ArrayList<Object>();
178 } else {
179 iElementPairs.clear();
180 }
181 iNotPrinter = false;
182 iNotParser = false;
183 iFieldFormatters = new FieldFormatter[10];
184 }
185
186
187
188
189
190
191 public PeriodFormatterBuilder append(PeriodFormatter formatter) {
192 if (formatter == null) {
193 throw new IllegalArgumentException("No formatter supplied");
194 }
195 clearPrefix();
196 append0(formatter.getPrinter(), formatter.getParser());
197 return this;
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211 public PeriodFormatterBuilder append(PeriodPrinter printer, PeriodParser parser) {
212 if (printer == null && parser == null) {
213 throw new IllegalArgumentException("No printer or parser supplied");
214 }
215 clearPrefix();
216 append0(printer, parser);
217 return this;
218 }
219
220
221
222
223
224
225
226
227 public PeriodFormatterBuilder appendLiteral(String text) {
228 if (text == null) {
229 throw new IllegalArgumentException("Literal must not be null");
230 }
231 clearPrefix();
232 Literal literal = new Literal(text);
233 append0(literal, literal);
234 return this;
235 }
236
237
238
239
240
241
242
243
244 public PeriodFormatterBuilder minimumPrintedDigits(int minDigits) {
245 iMinPrintedDigits = minDigits;
246 return this;
247 }
248
249
250
251
252
253
254
255 public PeriodFormatterBuilder maximumParsedDigits(int maxDigits) {
256 iMaxParsedDigits = maxDigits;
257 return this;
258 }
259
260
261
262
263
264
265 public PeriodFormatterBuilder rejectSignedValues(boolean v) {
266 iRejectSignedValues = v;
267 return this;
268 }
269
270
271
272
273
274
275
276
277
278
279 public PeriodFormatterBuilder printZeroRarelyLast() {
280 iPrintZeroSetting = PRINT_ZERO_RARELY_LAST;
281 return this;
282 }
283
284
285
286
287
288
289
290
291 public PeriodFormatterBuilder printZeroRarelyFirst() {
292 iPrintZeroSetting = PRINT_ZERO_RARELY_FIRST;
293 return this;
294 }
295
296
297
298
299
300
301
302 public PeriodFormatterBuilder printZeroIfSupported() {
303 iPrintZeroSetting = PRINT_ZERO_IF_SUPPORTED;
304 return this;
305 }
306
307
308
309
310
311
312
313
314 public PeriodFormatterBuilder printZeroAlways() {
315 iPrintZeroSetting = PRINT_ZERO_ALWAYS;
316 return this;
317 }
318
319
320
321
322
323
324
325
326
327
328 public PeriodFormatterBuilder printZeroNever() {
329 iPrintZeroSetting = PRINT_ZERO_NEVER;
330 return this;
331 }
332
333
334
335
336
337
338
339
340
341
342 public PeriodFormatterBuilder appendPrefix(String text) {
343 if (text == null) {
344 throw new IllegalArgumentException();
345 }
346 return appendPrefix(new SimpleAffix(text));
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361 public PeriodFormatterBuilder appendPrefix(String singularText,
362 String pluralText) {
363 if (singularText == null || pluralText == null) {
364 throw new IllegalArgumentException();
365 }
366 return appendPrefix(new PluralAffix(singularText, pluralText));
367 }
368
369
370
371
372
373
374
375
376
377 private PeriodFormatterBuilder appendPrefix(PeriodFieldAffix prefix) {
378 if (prefix == null) {
379 throw new IllegalArgumentException();
380 }
381 if (iPrefix != null) {
382 prefix = new CompositeAffix(iPrefix, prefix);
383 }
384 iPrefix = prefix;
385 return this;
386 }
387
388
389
390
391
392
393
394
395
396
397 public PeriodFormatterBuilder appendYears() {
398 appendField(YEARS);
399 return this;
400 }
401
402
403
404
405
406
407
408
409
410 public PeriodFormatterBuilder appendMonths() {
411 appendField(MONTHS);
412 return this;
413 }
414
415
416
417
418
419
420
421
422
423 public PeriodFormatterBuilder appendWeeks() {
424 appendField(WEEKS);
425 return this;
426 }
427
428
429
430
431
432
433
434
435
436 public PeriodFormatterBuilder appendDays() {
437 appendField(DAYS);
438 return this;
439 }
440
441
442
443
444
445
446
447
448
449 public PeriodFormatterBuilder appendHours() {
450 appendField(HOURS);
451 return this;
452 }
453
454
455
456
457
458
459
460
461
462 public PeriodFormatterBuilder appendMinutes() {
463 appendField(MINUTES);
464 return this;
465 }
466
467
468
469
470
471
472
473
474
475 public PeriodFormatterBuilder appendSeconds() {
476 appendField(SECONDS);
477 return this;
478 }
479
480
481
482
483
484
485
486
487 public PeriodFormatterBuilder appendSecondsWithMillis() {
488 appendField(SECONDS_MILLIS);
489 return this;
490 }
491
492
493
494
495
496
497
498
499 public PeriodFormatterBuilder appendSecondsWithOptionalMillis() {
500 appendField(SECONDS_OPTIONAL_MILLIS);
501 return this;
502 }
503
504
505
506
507
508
509
510
511
512 public PeriodFormatterBuilder appendMillis() {
513 appendField(MILLIS);
514 return this;
515 }
516
517
518
519
520
521
522
523
524 public PeriodFormatterBuilder appendMillis3Digit() {
525 appendField(7, 3);
526 return this;
527 }
528
529 private void appendField(int type) {
530 appendField(type, iMinPrintedDigits);
531 }
532
533 private void appendField(int type, int minPrinted) {
534 FieldFormatter field = new FieldFormatter(minPrinted, iPrintZeroSetting,
535 iMaxParsedDigits, iRejectSignedValues, type, iFieldFormatters, iPrefix, null);
536 append0(field, field);
537 iFieldFormatters[type] = field;
538 iPrefix = null;
539 }
540
541
542
543
544
545
546
547
548
549
550
551 public PeriodFormatterBuilder appendSuffix(String text) {
552 if (text == null) {
553 throw new IllegalArgumentException();
554 }
555 return appendSuffix(new SimpleAffix(text));
556 }
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571 public PeriodFormatterBuilder appendSuffix(String singularText,
572 String pluralText) {
573 if (singularText == null || pluralText == null) {
574 throw new IllegalArgumentException();
575 }
576 return appendSuffix(new PluralAffix(singularText, pluralText));
577 }
578
579
580
581
582
583
584
585
586
587
588 private PeriodFormatterBuilder appendSuffix(PeriodFieldAffix suffix) {
589 final Object originalPrinter;
590 final Object originalParser;
591 if (iElementPairs.size() > 0) {
592 originalPrinter = iElementPairs.get(iElementPairs.size() - 2);
593 originalParser = iElementPairs.get(iElementPairs.size() - 1);
594 } else {
595 originalPrinter = null;
596 originalParser = null;
597 }
598
599 if (originalPrinter == null || originalParser == null ||
600 originalPrinter != originalParser ||
601 !(originalPrinter instanceof FieldFormatter)) {
602 throw new IllegalStateException("No field to apply suffix to");
603 }
604
605 clearPrefix();
606 FieldFormatter newField = new FieldFormatter((FieldFormatter) originalPrinter, suffix);
607 iElementPairs.set(iElementPairs.size() - 2, newField);
608 iElementPairs.set(iElementPairs.size() - 1, newField);
609 iFieldFormatters[newField.getFieldType()] = newField;
610
611 return this;
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631 public PeriodFormatterBuilder appendSeparator(String text) {
632 return appendSeparator(text, text, null, true, true);
633 }
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651 public PeriodFormatterBuilder appendSeparatorIfFieldsAfter(String text) {
652 return appendSeparator(text, text, null, false, true);
653 }
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671 public PeriodFormatterBuilder appendSeparatorIfFieldsBefore(String text) {
672 return appendSeparator(text, text, null, true, false);
673 }
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696 public PeriodFormatterBuilder appendSeparator(String text, String finalText) {
697 return appendSeparator(text, finalText, null, true, true);
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722 public PeriodFormatterBuilder appendSeparator(String text, String finalText,
723 String[] variants) {
724 return appendSeparator(text, finalText, variants, true, true);
725 }
726
727 private PeriodFormatterBuilder appendSeparator(String text, String finalText,
728 String[] variants,
729 boolean useBefore, boolean useAfter) {
730 if (text == null || finalText == null) {
731 throw new IllegalArgumentException();
732 }
733
734 clearPrefix();
735
736
737 List<Object> pairs = iElementPairs;
738 if (pairs.size() == 0) {
739 if (useAfter && useBefore == false) {
740 Separator separator = new Separator(
741 text, finalText, variants,
742 Literal.EMPTY, Literal.EMPTY, useBefore, useAfter);
743 append0(separator, separator);
744 }
745 return this;
746 }
747
748
749 int i;
750 Separator lastSeparator = null;
751 for (i=pairs.size(); --i>=0; ) {
752 if (pairs.get(i) instanceof Separator) {
753 lastSeparator = (Separator) pairs.get(i);
754 pairs = pairs.subList(i + 1, pairs.size());
755 break;
756 }
757 i--;
758 }
759
760
761 if (lastSeparator != null && pairs.size() == 0) {
762 throw new IllegalStateException("Cannot have two adjacent separators");
763 } else {
764 Object[] comp = createComposite(pairs);
765 pairs.clear();
766 Separator separator = new Separator(
767 text, finalText, variants,
768 (PeriodPrinter) comp[0], (PeriodParser) comp[1],
769 useBefore, useAfter);
770 pairs.add(separator);
771 pairs.add(separator);
772 }
773
774 return this;
775 }
776
777
778 private void clearPrefix() throws IllegalStateException {
779 if (iPrefix != null) {
780 throw new IllegalStateException("Prefix not followed by field");
781 }
782 iPrefix = null;
783 }
784
785 private PeriodFormatterBuilder append0(PeriodPrinter printer, PeriodParser parser) {
786 iElementPairs.add(printer);
787 iElementPairs.add(parser);
788 iNotPrinter |= (printer == null);
789 iNotParser |= (parser == null);
790 return this;
791 }
792
793
794 private static PeriodFormatter toFormatter(List<Object> elementPairs, boolean notPrinter, boolean notParser) {
795 if (notPrinter && notParser) {
796 throw new IllegalStateException("Builder has created neither a printer nor a parser");
797 }
798 int size = elementPairs.size();
799 if (size >= 2 && elementPairs.get(0) instanceof Separator) {
800 Separator sep = (Separator) elementPairs.get(0);
801 if (sep.iAfterParser == null && sep.iAfterPrinter == null) {
802 PeriodFormatter f = toFormatter(elementPairs.subList(2, size), notPrinter, notParser);
803 sep = sep.finish(f.getPrinter(), f.getParser());
804 return new PeriodFormatter(sep, sep);
805 }
806 }
807 Object[] comp = createComposite(elementPairs);
808 if (notPrinter) {
809 return new PeriodFormatter(null, (PeriodParser) comp[1]);
810 } else if (notParser) {
811 return new PeriodFormatter((PeriodPrinter) comp[0], null);
812 } else {
813 return new PeriodFormatter((PeriodPrinter) comp[0], (PeriodParser) comp[1]);
814 }
815 }
816
817 private static Object[] createComposite(List<Object> elementPairs) {
818 switch (elementPairs.size()) {
819 case 0:
820 return new Object[] {Literal.EMPTY, Literal.EMPTY};
821 case 1:
822 return new Object[] {elementPairs.get(0), elementPairs.get(1)};
823 default:
824 Composite comp = new Composite(elementPairs);
825 return new Object[] {comp, comp};
826 }
827 }
828
829
830
831
832
833
834 static interface PeriodFieldAffix {
835 int calculatePrintedLength(int value);
836
837 void printTo(StringBuffer buf, int value);
838
839 void printTo(Writer out, int value) throws IOException;
840
841
842
843
844 int parse(String periodStr, int position);
845
846
847
848
849 int scan(String periodStr, int position);
850 }
851
852
853
854
855
856 static class SimpleAffix implements PeriodFieldAffix {
857 private final String iText;
858
859 SimpleAffix(String text) {
860 iText = text;
861 }
862
863 public int calculatePrintedLength(int value) {
864 return iText.length();
865 }
866
867 public void printTo(StringBuffer buf, int value) {
868 buf.append(iText);
869 }
870
871 public void printTo(Writer out, int value) throws IOException {
872 out.write(iText);
873 }
874
875 public int parse(String periodStr, int position) {
876 String text = iText;
877 int textLength = text.length();
878 if (periodStr.regionMatches(true, position, text, 0, textLength)) {
879 return position + textLength;
880 }
881 return ~position;
882 }
883
884 public int scan(String periodStr, final int position) {
885 String text = iText;
886 int textLength = text.length();
887 int sourceLength = periodStr.length();
888 search:
889 for (int pos = position; pos < sourceLength; pos++) {
890 if (periodStr.regionMatches(true, pos, text, 0, textLength)) {
891 return pos;
892 }
893
894 switch (periodStr.charAt(pos)) {
895 case '0': case '1': case '2': case '3': case '4':
896 case '5': case '6': case '7': case '8': case '9':
897 case '.': case ',': case '+': case '-':
898 break;
899 default:
900 break search;
901 }
902 }
903 return ~position;
904 }
905 }
906
907
908
909
910
911
912 static class PluralAffix implements PeriodFieldAffix {
913 private final String iSingularText;
914 private final String iPluralText;
915
916 PluralAffix(String singularText, String pluralText) {
917 iSingularText = singularText;
918 iPluralText = pluralText;
919 }
920
921 public int calculatePrintedLength(int value) {
922 return (value == 1 ? iSingularText : iPluralText).length();
923 }
924
925 public void printTo(StringBuffer buf, int value) {
926 buf.append(value == 1 ? iSingularText : iPluralText);
927 }
928
929 public void printTo(Writer out, int value) throws IOException {
930 out.write(value == 1 ? iSingularText : iPluralText);
931 }
932
933 public int parse(String periodStr, int position) {
934 String text1 = iPluralText;
935 String text2 = iSingularText;
936
937 if (text1.length() < text2.length()) {
938
939 String temp = text1;
940 text1 = text2;
941 text2 = temp;
942 }
943
944 if (periodStr.regionMatches
945 (true, position, text1, 0, text1.length())) {
946 return position + text1.length();
947 }
948 if (periodStr.regionMatches
949 (true, position, text2, 0, text2.length())) {
950 return position + text2.length();
951 }
952
953 return ~position;
954 }
955
956 public int scan(String periodStr, final int position) {
957 String text1 = iPluralText;
958 String text2 = iSingularText;
959
960 if (text1.length() < text2.length()) {
961
962 String temp = text1;
963 text1 = text2;
964 text2 = temp;
965 }
966
967 int textLength1 = text1.length();
968 int textLength2 = text2.length();
969
970 int sourceLength = periodStr.length();
971 for (int pos = position; pos < sourceLength; pos++) {
972 if (periodStr.regionMatches(true, pos, text1, 0, textLength1)) {
973 return pos;
974 }
975 if (periodStr.regionMatches(true, pos, text2, 0, textLength2)) {
976 return pos;
977 }
978 }
979 return ~position;
980 }
981 }
982
983
984
985
986
987 static class CompositeAffix implements PeriodFieldAffix {
988 private final PeriodFieldAffix iLeft;
989 private final PeriodFieldAffix iRight;
990
991 CompositeAffix(PeriodFieldAffix left, PeriodFieldAffix right) {
992 iLeft = left;
993 iRight = right;
994 }
995
996 public int calculatePrintedLength(int value) {
997 return iLeft.calculatePrintedLength(value)
998 + iRight.calculatePrintedLength(value);
999 }
1000
1001 public void printTo(StringBuffer buf, int value) {
1002 iLeft.printTo(buf, value);
1003 iRight.printTo(buf, value);
1004 }
1005
1006 public void printTo(Writer out, int value) throws IOException {
1007 iLeft.printTo(out, value);
1008 iRight.printTo(out, value);
1009 }
1010
1011 public int parse(String periodStr, int position) {
1012 position = iLeft.parse(periodStr, position);
1013 if (position >= 0) {
1014 position = iRight.parse(periodStr, position);
1015 }
1016 return position;
1017 }
1018
1019 public int scan(String periodStr, final int position) {
1020 int pos = iLeft.scan(periodStr, position);
1021 if (pos >= 0) {
1022 return iRight.scan(periodStr, pos);
1023 }
1024 return ~position;
1025 }
1026 }
1027
1028
1029
1030
1031
1032 static class FieldFormatter
1033 implements PeriodPrinter, PeriodParser {
1034 private final int iMinPrintedDigits;
1035 private final int iPrintZeroSetting;
1036 private final int iMaxParsedDigits;
1037 private final boolean iRejectSignedValues;
1038
1039
1040 private final int iFieldType;
1041
1042
1043
1044
1045 private final FieldFormatter[] iFieldFormatters;
1046
1047 private final PeriodFieldAffix iPrefix;
1048 private final PeriodFieldAffix iSuffix;
1049
1050 FieldFormatter(int minPrintedDigits, int printZeroSetting,
1051 int maxParsedDigits, boolean rejectSignedValues,
1052 int fieldType, FieldFormatter[] fieldFormatters,
1053 PeriodFieldAffix prefix, PeriodFieldAffix suffix) {
1054 iMinPrintedDigits = minPrintedDigits;
1055 iPrintZeroSetting = printZeroSetting;
1056 iMaxParsedDigits = maxParsedDigits;
1057 iRejectSignedValues = rejectSignedValues;
1058 iFieldType = fieldType;
1059 iFieldFormatters = fieldFormatters;
1060 iPrefix = prefix;
1061 iSuffix = suffix;
1062 }
1063
1064 FieldFormatter(FieldFormatter field, PeriodFieldAffix suffix) {
1065 iMinPrintedDigits = field.iMinPrintedDigits;
1066 iPrintZeroSetting = field.iPrintZeroSetting;
1067 iMaxParsedDigits = field.iMaxParsedDigits;
1068 iRejectSignedValues = field.iRejectSignedValues;
1069 iFieldType = field.iFieldType;
1070 iFieldFormatters = field.iFieldFormatters;
1071 iPrefix = field.iPrefix;
1072 if (field.iSuffix != null) {
1073 suffix = new CompositeAffix(field.iSuffix, suffix);
1074 }
1075 iSuffix = suffix;
1076 }
1077
1078 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) {
1079 if (stopAt <= 0) {
1080 return 0;
1081 }
1082 if (iPrintZeroSetting == PRINT_ZERO_ALWAYS || getFieldValue(period) != Long.MAX_VALUE) {
1083 return 1;
1084 }
1085 return 0;
1086 }
1087
1088 public int calculatePrintedLength(ReadablePeriod period, Locale locale) {
1089 long valueLong = getFieldValue(period);
1090 if (valueLong == Long.MAX_VALUE) {
1091 return 0;
1092 }
1093
1094 int sum = Math.max(FormatUtils.calculateDigitCount(valueLong), iMinPrintedDigits);
1095 if (iFieldType >= SECONDS_MILLIS) {
1096
1097
1098 sum = (valueLong < 0 ? Math.max(sum, 5) : Math.max(sum, 4));
1099
1100 sum++;
1101 if (iFieldType == SECONDS_OPTIONAL_MILLIS &&
1102 (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND) == 0) {
1103 sum -= 4;
1104 }
1105
1106 valueLong = valueLong / DateTimeConstants.MILLIS_PER_SECOND;
1107 }
1108 int value = (int) valueLong;
1109
1110 if (iPrefix != null) {
1111 sum += iPrefix.calculatePrintedLength(value);
1112 }
1113 if (iSuffix != null) {
1114 sum += iSuffix.calculatePrintedLength(value);
1115 }
1116
1117 return sum;
1118 }
1119
1120 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) {
1121 long valueLong = getFieldValue(period);
1122 if (valueLong == Long.MAX_VALUE) {
1123 return;
1124 }
1125 int value = (int) valueLong;
1126 if (iFieldType >= SECONDS_MILLIS) {
1127 value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND);
1128 }
1129
1130 if (iPrefix != null) {
1131 iPrefix.printTo(buf, value);
1132 }
1133 int bufLen = buf.length();
1134 int minDigits = iMinPrintedDigits;
1135 if (minDigits <= 1) {
1136 FormatUtils.appendUnpaddedInteger(buf, value);
1137 } else {
1138 FormatUtils.appendPaddedInteger(buf, value, minDigits);
1139 }
1140 if (iFieldType >= SECONDS_MILLIS) {
1141 int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND);
1142 if (iFieldType == SECONDS_MILLIS || dp > 0) {
1143 if (valueLong < 0 && valueLong > -DateTimeConstants.MILLIS_PER_SECOND) {
1144 buf.insert(bufLen, '-');
1145 }
1146 buf.append('.');
1147 FormatUtils.appendPaddedInteger(buf, dp, 3);
1148 }
1149 }
1150 if (iSuffix != null) {
1151 iSuffix.printTo(buf, value);
1152 }
1153 }
1154
1155 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException {
1156 long valueLong = getFieldValue(period);
1157 if (valueLong == Long.MAX_VALUE) {
1158 return;
1159 }
1160 int value = (int) valueLong;
1161 if (iFieldType >= SECONDS_MILLIS) {
1162 value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND);
1163 }
1164
1165 if (iPrefix != null) {
1166 iPrefix.printTo(out, value);
1167 }
1168 int minDigits = iMinPrintedDigits;
1169 if (minDigits <= 1) {
1170 FormatUtils.writeUnpaddedInteger(out, value);
1171 } else {
1172 FormatUtils.writePaddedInteger(out, value, minDigits);
1173 }
1174 if (iFieldType >= SECONDS_MILLIS) {
1175 int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND);
1176 if (iFieldType == SECONDS_MILLIS || dp > 0) {
1177 out.write('.');
1178 FormatUtils.writePaddedInteger(out, dp, 3);
1179 }
1180 }
1181 if (iSuffix != null) {
1182 iSuffix.printTo(out, value);
1183 }
1184 }
1185
1186 public int parseInto(
1187 ReadWritablePeriod period, String text,
1188 int position, Locale locale) {
1189
1190 boolean mustParse = (iPrintZeroSetting == PRINT_ZERO_ALWAYS);
1191
1192
1193 if (position >= text.length()) {
1194 return mustParse ? ~position : position;
1195 }
1196
1197 if (iPrefix != null) {
1198 position = iPrefix.parse(text, position);
1199 if (position >= 0) {
1200
1201 mustParse = true;
1202 } else {
1203
1204 if (!mustParse) {
1205
1206
1207
1208 return ~position;
1209 }
1210 return position;
1211 }
1212 }
1213
1214 int suffixPos = -1;
1215 if (iSuffix != null && !mustParse) {
1216
1217
1218 suffixPos = iSuffix.scan(text, position);
1219 if (suffixPos >= 0) {
1220
1221 mustParse = true;
1222 } else {
1223
1224 if (!mustParse) {
1225
1226
1227
1228 return ~suffixPos;
1229 }
1230 return suffixPos;
1231 }
1232 }
1233
1234 if (!mustParse && !isSupported(period.getPeriodType(), iFieldType)) {
1235
1236
1237 return position;
1238 }
1239
1240 int limit;
1241 if (suffixPos > 0) {
1242 limit = Math.min(iMaxParsedDigits, suffixPos - position);
1243 } else {
1244 limit = Math.min(iMaxParsedDigits, text.length() - position);
1245 }
1246
1247
1248 int length = 0;
1249 int fractPos = -1;
1250 boolean hasDigits = false;
1251 while (length < limit) {
1252 char c = text.charAt(position + length);
1253
1254 if (length == 0 && (c == '-' || c == '+') && !iRejectSignedValues) {
1255 boolean negative = c == '-';
1256
1257
1258 if (length + 1 >= limit ||
1259 (c = text.charAt(position + length + 1)) < '0' || c > '9')
1260 {
1261 break;
1262 }
1263
1264 if (negative) {
1265 length++;
1266 } else {
1267
1268 position++;
1269 }
1270
1271 limit = Math.min(limit + 1, text.length() - position);
1272 continue;
1273 }
1274
1275 if (c >= '0' && c <= '9') {
1276 hasDigits = true;
1277 } else {
1278 if ((c == '.' || c == ',')
1279 && (iFieldType == SECONDS_MILLIS || iFieldType == SECONDS_OPTIONAL_MILLIS)) {
1280 if (fractPos >= 0) {
1281
1282 break;
1283 }
1284 fractPos = position + length + 1;
1285
1286 limit = Math.min(limit + 1, text.length() - position);
1287 } else {
1288 break;
1289 }
1290 }
1291 length++;
1292 }
1293
1294 if (!hasDigits) {
1295 return ~position;
1296 }
1297
1298 if (suffixPos >= 0 && position + length != suffixPos) {
1299
1300
1301
1302
1303 return position;
1304 }
1305
1306 if (iFieldType != SECONDS_MILLIS && iFieldType != SECONDS_OPTIONAL_MILLIS) {
1307
1308 setFieldValue(period, iFieldType, parseInt(text, position, length));
1309 } else if (fractPos < 0) {
1310 setFieldValue(period, SECONDS, parseInt(text, position, length));
1311 setFieldValue(period, MILLIS, 0);
1312 } else {
1313 int wholeValue = parseInt(text, position, fractPos - position - 1);
1314 setFieldValue(period, SECONDS, wholeValue);
1315
1316 int fractLen = position + length - fractPos;
1317 int fractValue;
1318 if (fractLen <= 0) {
1319 fractValue = 0;
1320 } else {
1321 if (fractLen >= 3) {
1322 fractValue = parseInt(text, fractPos, 3);
1323 } else {
1324 fractValue = parseInt(text, fractPos, fractLen);
1325 if (fractLen == 1) {
1326 fractValue *= 100;
1327 } else {
1328 fractValue *= 10;
1329 }
1330 }
1331 if (wholeValue < 0) {
1332 fractValue = -fractValue;
1333 }
1334 }
1335
1336 setFieldValue(period, MILLIS, fractValue);
1337 }
1338
1339 position += length;
1340
1341 if (position >= 0 && iSuffix != null) {
1342 position = iSuffix.parse(text, position);
1343 }
1344
1345 return position;
1346 }
1347
1348
1349
1350
1351
1352
1353
1354 private int parseInt(String text, int position, int length) {
1355 if (length >= 10) {
1356
1357 return Integer.parseInt(text.substring(position, position + length));
1358 }
1359 if (length <= 0) {
1360 return 0;
1361 }
1362 int value = text.charAt(position++);
1363 length--;
1364 boolean negative;
1365 if (value == '-') {
1366 if (--length < 0) {
1367 return 0;
1368 }
1369 negative = true;
1370 value = text.charAt(position++);
1371 } else {
1372 negative = false;
1373 }
1374 value -= '0';
1375 while (length-- > 0) {
1376 value = ((value << 3) + (value << 1)) + text.charAt(position++) - '0';
1377 }
1378 return negative ? -value : value;
1379 }
1380
1381
1382
1383
1384 long getFieldValue(ReadablePeriod period) {
1385 PeriodType type;
1386 if (iPrintZeroSetting == PRINT_ZERO_ALWAYS) {
1387 type = null;
1388 } else {
1389 type = period.getPeriodType();
1390 }
1391 if (type != null && isSupported(type, iFieldType) == false) {
1392 return Long.MAX_VALUE;
1393 }
1394
1395 long value;
1396
1397 switch (iFieldType) {
1398 default:
1399 return Long.MAX_VALUE;
1400 case YEARS:
1401 value = period.get(DurationFieldType.years());
1402 break;
1403 case MONTHS:
1404 value = period.get(DurationFieldType.months());
1405 break;
1406 case WEEKS:
1407 value = period.get(DurationFieldType.weeks());
1408 break;
1409 case DAYS:
1410 value = period.get(DurationFieldType.days());
1411 break;
1412 case HOURS:
1413 value = period.get(DurationFieldType.hours());
1414 break;
1415 case MINUTES:
1416 value = period.get(DurationFieldType.minutes());
1417 break;
1418 case SECONDS:
1419 value = period.get(DurationFieldType.seconds());
1420 break;
1421 case MILLIS:
1422 value = period.get(DurationFieldType.millis());
1423 break;
1424 case SECONDS_MILLIS:
1425 case SECONDS_OPTIONAL_MILLIS:
1426 int seconds = period.get(DurationFieldType.seconds());
1427 int millis = period.get(DurationFieldType.millis());
1428 value = (seconds * (long) DateTimeConstants.MILLIS_PER_SECOND) + millis;
1429 break;
1430 }
1431
1432
1433 if (value == 0) {
1434 switch (iPrintZeroSetting) {
1435 case PRINT_ZERO_NEVER:
1436 return Long.MAX_VALUE;
1437 case PRINT_ZERO_RARELY_LAST:
1438 if (isZero(period) && iFieldFormatters[iFieldType] == this) {
1439 for (int i = iFieldType + 1; i <= MAX_FIELD; i++) {
1440 if (isSupported(type, i) && iFieldFormatters[i] != null) {
1441 return Long.MAX_VALUE;
1442 }
1443 }
1444 } else {
1445 return Long.MAX_VALUE;
1446 }
1447 break;
1448 case PRINT_ZERO_RARELY_FIRST:
1449 if (isZero(period) && iFieldFormatters[iFieldType] == this) {
1450 int i = Math.min(iFieldType, 8);
1451 i--;
1452 for (; i >= 0 && i <= MAX_FIELD; i--) {
1453 if (isSupported(type, i) && iFieldFormatters[i] != null) {
1454 return Long.MAX_VALUE;
1455 }
1456 }
1457 } else {
1458 return Long.MAX_VALUE;
1459 }
1460 break;
1461 }
1462 }
1463
1464 return value;
1465 }
1466
1467 boolean isZero(ReadablePeriod period) {
1468 for (int i = 0, isize = period.size(); i < isize; i++) {
1469 if (period.getValue(i) != 0) {
1470 return false;
1471 }
1472 }
1473 return true;
1474 }
1475
1476 boolean isSupported(PeriodType type, int field) {
1477 switch (field) {
1478 default:
1479 return false;
1480 case YEARS:
1481 return type.isSupported(DurationFieldType.years());
1482 case MONTHS:
1483 return type.isSupported(DurationFieldType.months());
1484 case WEEKS:
1485 return type.isSupported(DurationFieldType.weeks());
1486 case DAYS:
1487 return type.isSupported(DurationFieldType.days());
1488 case HOURS:
1489 return type.isSupported(DurationFieldType.hours());
1490 case MINUTES:
1491 return type.isSupported(DurationFieldType.minutes());
1492 case SECONDS:
1493 return type.isSupported(DurationFieldType.seconds());
1494 case MILLIS:
1495 return type.isSupported(DurationFieldType.millis());
1496 case SECONDS_MILLIS:
1497 case SECONDS_OPTIONAL_MILLIS:
1498 return type.isSupported(DurationFieldType.seconds()) ||
1499 type.isSupported(DurationFieldType.millis());
1500 }
1501 }
1502
1503 void setFieldValue(ReadWritablePeriod period, int field, int value) {
1504 switch (field) {
1505 default:
1506 break;
1507 case YEARS:
1508 period.setYears(value);
1509 break;
1510 case MONTHS:
1511 period.setMonths(value);
1512 break;
1513 case WEEKS:
1514 period.setWeeks(value);
1515 break;
1516 case DAYS:
1517 period.setDays(value);
1518 break;
1519 case HOURS:
1520 period.setHours(value);
1521 break;
1522 case MINUTES:
1523 period.setMinutes(value);
1524 break;
1525 case SECONDS:
1526 period.setSeconds(value);
1527 break;
1528 case MILLIS:
1529 period.setMillis(value);
1530 break;
1531 }
1532 }
1533
1534 int getFieldType() {
1535 return iFieldType;
1536 }
1537 }
1538
1539
1540
1541
1542
1543 static class Literal
1544 implements PeriodPrinter, PeriodParser {
1545 static final Literal EMPTY = new Literal("");
1546 private final String iText;
1547
1548 Literal(String text) {
1549 iText = text;
1550 }
1551
1552 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) {
1553 return 0;
1554 }
1555
1556 public int calculatePrintedLength(ReadablePeriod period, Locale locale) {
1557 return iText.length();
1558 }
1559
1560 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) {
1561 buf.append(iText);
1562 }
1563
1564 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException {
1565 out.write(iText);
1566 }
1567
1568 public int parseInto(
1569 ReadWritablePeriod period, String periodStr,
1570 int position, Locale locale) {
1571 if (periodStr.regionMatches(true, position, iText, 0, iText.length())) {
1572 return position + iText.length();
1573 }
1574 return ~position;
1575 }
1576 }
1577
1578
1579
1580
1581
1582
1583 static class Separator
1584 implements PeriodPrinter, PeriodParser {
1585 private final String iText;
1586 private final String iFinalText;
1587 private final String[] iParsedForms;
1588
1589 private final boolean iUseBefore;
1590 private final boolean iUseAfter;
1591
1592 private final PeriodPrinter iBeforePrinter;
1593 private volatile PeriodPrinter iAfterPrinter;
1594 private final PeriodParser iBeforeParser;
1595 private volatile PeriodParser iAfterParser;
1596
1597 Separator(String text, String finalText, String[] variants,
1598 PeriodPrinter beforePrinter, PeriodParser beforeParser,
1599 boolean useBefore, boolean useAfter) {
1600 iText = text;
1601 iFinalText = finalText;
1602
1603 if ((finalText == null || text.equals(finalText)) &&
1604 (variants == null || variants.length == 0)) {
1605
1606 iParsedForms = new String[] {text};
1607 } else {
1608
1609 TreeSet<String> parsedSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
1610 parsedSet.add(text);
1611 parsedSet.add(finalText);
1612 if (variants != null) {
1613 for (int i=variants.length; --i>=0; ) {
1614 parsedSet.add(variants[i]);
1615 }
1616 }
1617 ArrayList<String> parsedList = new ArrayList<String>(parsedSet);
1618 Collections.reverse(parsedList);
1619 iParsedForms = parsedList.toArray(new String[parsedList.size()]);
1620 }
1621
1622 iBeforePrinter = beforePrinter;
1623 iBeforeParser = beforeParser;
1624 iUseBefore = useBefore;
1625 iUseAfter = useAfter;
1626 }
1627
1628 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) {
1629 int sum = iBeforePrinter.countFieldsToPrint(period, stopAt, locale);
1630 if (sum < stopAt) {
1631 sum += iAfterPrinter.countFieldsToPrint(period, stopAt, locale);
1632 }
1633 return sum;
1634 }
1635
1636 public int calculatePrintedLength(ReadablePeriod period, Locale locale) {
1637 PeriodPrinter before = iBeforePrinter;
1638 PeriodPrinter after = iAfterPrinter;
1639
1640 int sum = before.calculatePrintedLength(period, locale)
1641 + after.calculatePrintedLength(period, locale);
1642
1643 if (iUseBefore) {
1644 if (before.countFieldsToPrint(period, 1, locale) > 0) {
1645 if (iUseAfter) {
1646 int afterCount = after.countFieldsToPrint(period, 2, locale);
1647 if (afterCount > 0) {
1648 sum += (afterCount > 1 ? iText : iFinalText).length();
1649 }
1650 } else {
1651 sum += iText.length();
1652 }
1653 }
1654 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
1655 sum += iText.length();
1656 }
1657
1658 return sum;
1659 }
1660
1661 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) {
1662 PeriodPrinter before = iBeforePrinter;
1663 PeriodPrinter after = iAfterPrinter;
1664
1665 before.printTo(buf, period, locale);
1666 if (iUseBefore) {
1667 if (before.countFieldsToPrint(period, 1, locale) > 0) {
1668 if (iUseAfter) {
1669 int afterCount = after.countFieldsToPrint(period, 2, locale);
1670 if (afterCount > 0) {
1671 buf.append(afterCount > 1 ? iText : iFinalText);
1672 }
1673 } else {
1674 buf.append(iText);
1675 }
1676 }
1677 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
1678 buf.append(iText);
1679 }
1680 after.printTo(buf, period, locale);
1681 }
1682
1683 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException {
1684 PeriodPrinter before = iBeforePrinter;
1685 PeriodPrinter after = iAfterPrinter;
1686
1687 before.printTo(out, period, locale);
1688 if (iUseBefore) {
1689 if (before.countFieldsToPrint(period, 1, locale) > 0) {
1690 if (iUseAfter) {
1691 int afterCount = after.countFieldsToPrint(period, 2, locale);
1692 if (afterCount > 0) {
1693 out.write(afterCount > 1 ? iText : iFinalText);
1694 }
1695 } else {
1696 out.write(iText);
1697 }
1698 }
1699 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
1700 out.write(iText);
1701 }
1702 after.printTo(out, period, locale);
1703 }
1704
1705 public int parseInto(
1706 ReadWritablePeriod period, String periodStr,
1707 int position, Locale locale) {
1708 int oldPos = position;
1709 position = iBeforeParser.parseInto(period, periodStr, position, locale);
1710
1711 if (position < 0) {
1712 return position;
1713 }
1714
1715 boolean found = false;
1716 int parsedFormLength = -1;
1717 if (position > oldPos) {
1718
1719 String[] parsedForms = iParsedForms;
1720 int length = parsedForms.length;
1721 for (int i=0; i < length; i++) {
1722 String parsedForm = parsedForms[i];
1723 if ((parsedForm == null || parsedForm.length() == 0) ||
1724 periodStr.regionMatches
1725 (true, position, parsedForm, 0, parsedForm.length())) {
1726
1727 parsedFormLength = (parsedForm == null ? 0 : parsedForm.length());
1728 position += parsedFormLength;
1729 found = true;
1730 break;
1731 }
1732 }
1733 }
1734
1735 oldPos = position;
1736 position = iAfterParser.parseInto(period, periodStr, position, locale);
1737
1738 if (position < 0) {
1739 return position;
1740 }
1741
1742 if (found && position == oldPos && parsedFormLength > 0) {
1743
1744 return ~oldPos;
1745 }
1746
1747 if (position > oldPos && !found && !iUseBefore) {
1748
1749 return ~oldPos;
1750 }
1751
1752 return position;
1753 }
1754
1755 Separator finish(PeriodPrinter afterPrinter, PeriodParser afterParser) {
1756 iAfterPrinter = afterPrinter;
1757 iAfterParser = afterParser;
1758 return this;
1759 }
1760 }
1761
1762
1763
1764
1765
1766 static class Composite
1767 implements PeriodPrinter, PeriodParser {
1768
1769 private final PeriodPrinter[] iPrinters;
1770 private final PeriodParser[] iParsers;
1771
1772 Composite(List<Object> elementPairs) {
1773 List<Object> printerList = new ArrayList<Object>();
1774 List<Object> parserList = new ArrayList<Object>();
1775
1776 decompose(elementPairs, printerList, parserList);
1777
1778 if (printerList.size() <= 0) {
1779 iPrinters = null;
1780 } else {
1781 iPrinters = printerList.toArray(
1782 new PeriodPrinter[printerList.size()]);
1783 }
1784
1785 if (parserList.size() <= 0) {
1786 iParsers = null;
1787 } else {
1788 iParsers = parserList.toArray(
1789 new PeriodParser[parserList.size()]);
1790 }
1791 }
1792
1793 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) {
1794 int sum = 0;
1795 PeriodPrinter[] printers = iPrinters;
1796 for (int i=printers.length; sum < stopAt && --i>=0; ) {
1797 sum += printers[i].countFieldsToPrint(period, Integer.MAX_VALUE, locale);
1798 }
1799 return sum;
1800 }
1801
1802 public int calculatePrintedLength(ReadablePeriod period, Locale locale) {
1803 int sum = 0;
1804 PeriodPrinter[] printers = iPrinters;
1805 for (int i=printers.length; --i>=0; ) {
1806 sum += printers[i].calculatePrintedLength(period, locale);
1807 }
1808 return sum;
1809 }
1810
1811 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) {
1812 PeriodPrinter[] printers = iPrinters;
1813 int len = printers.length;
1814 for (int i=0; i<len; i++) {
1815 printers[i].printTo(buf, period, locale);
1816 }
1817 }
1818
1819 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException {
1820 PeriodPrinter[] printers = iPrinters;
1821 int len = printers.length;
1822 for (int i=0; i<len; i++) {
1823 printers[i].printTo(out, period, locale);
1824 }
1825 }
1826
1827 public int parseInto(
1828 ReadWritablePeriod period, String periodStr,
1829 int position, Locale locale) {
1830 PeriodParser[] parsers = iParsers;
1831 if (parsers == null) {
1832 throw new UnsupportedOperationException();
1833 }
1834
1835 int len = parsers.length;
1836 for (int i=0; i<len && position >= 0; i++) {
1837 position = parsers[i].parseInto(period, periodStr, position, locale);
1838 }
1839 return position;
1840 }
1841
1842 private void decompose(List<Object> elementPairs, List<Object> printerList, List<Object> parserList) {
1843 int size = elementPairs.size();
1844 for (int i=0; i<size; i+=2) {
1845 Object element = elementPairs.get(i);
1846 if (element instanceof PeriodPrinter) {
1847 if (element instanceof Composite) {
1848 addArrayToList(printerList, ((Composite) element).iPrinters);
1849 } else {
1850 printerList.add(element);
1851 }
1852 }
1853
1854 element = elementPairs.get(i + 1);
1855 if (element instanceof PeriodParser) {
1856 if (element instanceof Composite) {
1857 addArrayToList(parserList, ((Composite) element).iParsers);
1858 } else {
1859 parserList.add(element);
1860 }
1861 }
1862 }
1863 }
1864
1865 private void addArrayToList(List<Object> list, Object[] array) {
1866 if (array != null) {
1867 for (int i=0; i<array.length; i++) {
1868 list.add(array[i]);
1869 }
1870 }
1871 }
1872 }
1873
1874 }