1 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 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 83 private int iMinPrintedDigits; 84 private int iPrintZeroSetting; 85 private int iMaxParsedDigits; 86 private boolean iRejectSignedValues; 87 88 private PeriodFieldAffix iPrefix; 89 90 private List iElementPairs; 92 93 private boolean iNotPrinter; 94 95 private boolean iNotParser; 96 97 private FieldFormatter[] iFieldFormatters; 99 100 public PeriodFormatterBuilder() { 101 clear(); 102 } 103 104 121 public PeriodFormatter toFormatter() { 122 PeriodFormatter formatter = toFormatter(iElementPairs, iNotPrinter, iNotParser); 123 iFieldFormatters = (FieldFormatter[]) iFieldFormatters.clone(); 124 return formatter; 125 } 126 127 139 public PeriodPrinter toPrinter() { 140 if (iNotPrinter) { 141 return null; 142 } 143 return toFormatter().getPrinter(); 144 } 145 146 158 public PeriodParser toParser() { 159 if (iNotParser) { 160 return null; 161 } 162 return toFormatter().getParser(); 163 } 164 165 169 public void clear() { 170 iMinPrintedDigits = 1; 171 iPrintZeroSetting = PRINT_ZERO_RARELY_LAST; 172 iMaxParsedDigits = 10; 173 iRejectSignedValues = false; 174 iPrefix = null; 175 if (iElementPairs == null) { 176 iElementPairs = new ArrayList (); 177 } else { 178 iElementPairs.clear(); 179 } 180 iNotPrinter = false; 181 iNotParser = false; 182 iFieldFormatters = new FieldFormatter[10]; 183 } 184 185 190 public PeriodFormatterBuilder append(PeriodFormatter formatter) { 191 if (formatter == null) { 192 throw new IllegalArgumentException ("No formatter supplied"); 193 } 194 clearPrefix(); 195 append0(formatter.getPrinter(), formatter.getParser()); 196 return this; 197 } 198 199 210 public PeriodFormatterBuilder append(PeriodPrinter printer, PeriodParser parser) { 211 if (printer == null && parser == null) { 212 throw new IllegalArgumentException ("No printer or parser supplied"); 213 } 214 clearPrefix(); 215 append0(printer, parser); 216 return this; 217 } 218 219 226 public PeriodFormatterBuilder appendLiteral(String text) { 227 if (text == null) { 228 throw new IllegalArgumentException ("Literal must not be null"); 229 } 230 clearPrefix(); 231 Literal literal = new Literal(text); 232 append0(literal, literal); 233 return this; 234 } 235 236 243 public PeriodFormatterBuilder minimumPrintedDigits(int minDigits) { 244 iMinPrintedDigits = minDigits; 245 return this; 246 } 247 248 254 public PeriodFormatterBuilder maximumParsedDigits(int maxDigits) { 255 iMaxParsedDigits = maxDigits; 256 return this; 257 } 258 259 264 public PeriodFormatterBuilder rejectSignedValues(boolean v) { 265 iRejectSignedValues = v; 266 return this; 267 } 268 269 278 public PeriodFormatterBuilder printZeroRarelyLast() { 279 iPrintZeroSetting = PRINT_ZERO_RARELY_LAST; 280 return this; 281 } 282 283 290 public PeriodFormatterBuilder printZeroRarelyFirst() { 291 iPrintZeroSetting = PRINT_ZERO_RARELY_FIRST; 292 return this; 293 } 294 295 301 public PeriodFormatterBuilder printZeroIfSupported() { 302 iPrintZeroSetting = PRINT_ZERO_IF_SUPPORTED; 303 return this; 304 } 305 306 313 public PeriodFormatterBuilder printZeroAlways() { 314 iPrintZeroSetting = PRINT_ZERO_ALWAYS; 315 return this; 316 } 317 318 327 public PeriodFormatterBuilder printZeroNever() { 328 iPrintZeroSetting = PRINT_ZERO_NEVER; 329 return this; 330 } 331 332 341 public PeriodFormatterBuilder appendPrefix(String text) { 342 if (text == null) { 343 throw new IllegalArgumentException (); 344 } 345 return appendPrefix(new SimpleAffix(text)); 346 } 347 348 360 public PeriodFormatterBuilder appendPrefix(String singularText, 361 String pluralText) { 362 if (singularText == null || pluralText == null) { 363 throw new IllegalArgumentException (); 364 } 365 return appendPrefix(new PluralAffix(singularText, pluralText)); 366 } 367 368 376 private PeriodFormatterBuilder appendPrefix(PeriodFieldAffix prefix) { 377 if (prefix == null) { 378 throw new IllegalArgumentException (); 379 } 380 if (iPrefix != null) { 381 prefix = new CompositeAffix(iPrefix, prefix); 382 } 383 iPrefix = prefix; 384 return this; 385 } 386 387 396 public PeriodFormatterBuilder appendYears() { 397 appendField(YEARS); 398 return this; 399 } 400 401 409 public PeriodFormatterBuilder appendMonths() { 410 appendField(MONTHS); 411 return this; 412 } 413 414 422 public PeriodFormatterBuilder appendWeeks() { 423 appendField(WEEKS); 424 return this; 425 } 426 427 435 public PeriodFormatterBuilder appendDays() { 436 appendField(DAYS); 437 return this; 438 } 439 440 448 public PeriodFormatterBuilder appendHours() { 449 appendField(HOURS); 450 return this; 451 } 452 453 461 public PeriodFormatterBuilder appendMinutes() { 462 appendField(MINUTES); 463 return this; 464 } 465 466 474 public PeriodFormatterBuilder appendSeconds() { 475 appendField(SECONDS); 476 return this; 477 } 478 479 486 public PeriodFormatterBuilder appendSecondsWithMillis() { 487 appendField(SECONDS_MILLIS); 488 return this; 489 } 490 491 498 public PeriodFormatterBuilder appendSecondsWithOptionalMillis() { 499 appendField(SECONDS_OPTIONAL_MILLIS); 500 return this; 501 } 502 503 511 public PeriodFormatterBuilder appendMillis() { 512 appendField(MILLIS); 513 return this; 514 } 515 516 523 public PeriodFormatterBuilder appendMillis3Digit() { 524 appendField(7, 3); 525 return this; 526 } 527 528 private void appendField(int type) { 529 appendField(type, iMinPrintedDigits); 530 } 531 532 private void appendField(int type, int minPrinted) { 533 FieldFormatter field = new FieldFormatter(minPrinted, iPrintZeroSetting, 534 iMaxParsedDigits, iRejectSignedValues, type, iFieldFormatters, iPrefix, null); 535 append0(field, field); 536 iFieldFormatters[type] = field; 537 iPrefix = null; 538 } 539 540 550 public PeriodFormatterBuilder appendSuffix(String text) { 551 if (text == null) { 552 throw new IllegalArgumentException (); 553 } 554 return appendSuffix(new SimpleAffix(text)); 555 } 556 557 570 public PeriodFormatterBuilder appendSuffix(String singularText, 571 String pluralText) { 572 if (singularText == null || pluralText == null) { 573 throw new IllegalArgumentException (); 574 } 575 return appendSuffix(new PluralAffix(singularText, pluralText)); 576 } 577 578 587 private PeriodFormatterBuilder appendSuffix(PeriodFieldAffix suffix) { 588 final Object originalPrinter; 589 final Object originalParser; 590 if (iElementPairs.size() > 0) { 591 originalPrinter = iElementPairs.get(iElementPairs.size() - 2); 592 originalParser = iElementPairs.get(iElementPairs.size() - 1); 593 } else { 594 originalPrinter = null; 595 originalParser = null; 596 } 597 598 if (originalPrinter == null || originalParser == null || 599 originalPrinter != originalParser || 600 !(originalPrinter instanceof FieldFormatter)) { 601 throw new IllegalStateException ("No field to apply suffix to"); 602 } 603 604 clearPrefix(); 605 FieldFormatter newField = new FieldFormatter((FieldFormatter) originalPrinter, suffix); 606 iElementPairs.set(iElementPairs.size() - 2, newField); 607 iElementPairs.set(iElementPairs.size() - 1, newField); 608 iFieldFormatters[newField.getFieldType()] = newField; 609 610 return this; 611 } 612 613 630 public PeriodFormatterBuilder appendSeparator(String text) { 631 return appendSeparator(text, text, null, true, true); 632 } 633 634 650 public PeriodFormatterBuilder appendSeparatorIfFieldsAfter(String text) { 651 return appendSeparator(text, text, null, false, true); 652 } 653 654 670 public PeriodFormatterBuilder appendSeparatorIfFieldsBefore(String text) { 671 return appendSeparator(text, text, null, true, false); 672 } 673 674 695 public PeriodFormatterBuilder appendSeparator(String text, String finalText) { 696 return appendSeparator(text, finalText, null, true, true); 697 } 698 699 721 public PeriodFormatterBuilder appendSeparator(String text, String finalText, 722 String [] variants) { 723 return appendSeparator(text, finalText, variants, true, true); 724 } 725 726 private PeriodFormatterBuilder appendSeparator(String text, String finalText, 727 String [] variants, 728 boolean useBefore, boolean useAfter) { 729 if (text == null || finalText == null) { 730 throw new IllegalArgumentException (); 731 } 732 733 clearPrefix(); 734 735 List pairs = iElementPairs; 737 if (pairs.size() == 0) { 738 if (useAfter && useBefore == false) { 739 Separator separator = new Separator( 740 text, finalText, variants, 741 Literal.EMPTY, Literal.EMPTY, useBefore, useAfter); 742 append0(separator, separator); 743 } 744 return this; 745 } 746 747 int i; 749 Separator lastSeparator = null; 750 for (i=pairs.size(); --i>=0; ) { 751 if (pairs.get(i) instanceof Separator) { 752 lastSeparator = (Separator) pairs.get(i); 753 pairs = pairs.subList(i + 1, pairs.size()); 754 break; 755 } 756 i--; } 758 759 if (lastSeparator != null && pairs.size() == 0) { 761 throw new IllegalStateException ("Cannot have two adjacent separators"); 762 } else { 763 Object [] comp = createComposite(pairs); 764 pairs.clear(); 765 Separator separator = new Separator( 766 text, finalText, variants, 767 (PeriodPrinter) comp[0], (PeriodParser) comp[1], 768 useBefore, useAfter); 769 pairs.add(separator); 770 pairs.add(separator); 771 } 772 773 return this; 774 } 775 776 private void clearPrefix() throws IllegalStateException { 778 if (iPrefix != null) { 779 throw new IllegalStateException ("Prefix not followed by field"); 780 } 781 iPrefix = null; 782 } 783 784 private PeriodFormatterBuilder append0(PeriodPrinter printer, PeriodParser parser) { 785 iElementPairs.add(printer); 786 iElementPairs.add(parser); 787 iNotPrinter |= (printer == null); 788 iNotParser |= (parser == null); 789 return this; 790 } 791 792 private static PeriodFormatter toFormatter(List elementPairs, boolean notPrinter, boolean notParser) { 794 if (notPrinter && notParser) { 795 throw new IllegalStateException ("Builder has created neither a printer nor a parser"); 796 } 797 int size = elementPairs.size(); 798 if (size >= 2 && elementPairs.get(0) instanceof Separator) { 799 Separator sep = (Separator) elementPairs.get(0); 800 PeriodFormatter f = toFormatter(elementPairs.subList(2, size), notPrinter, notParser); 801 sep = sep.finish(f.getPrinter(), f.getParser()); 802 return new PeriodFormatter(sep, sep); 803 } 804 Object [] comp = createComposite(elementPairs); 805 if (notPrinter) { 806 return new PeriodFormatter(null, (PeriodParser) comp[1]); 807 } else if (notParser) { 808 return new PeriodFormatter((PeriodPrinter) comp[0], null); 809 } else { 810 return new PeriodFormatter((PeriodPrinter) comp[0], (PeriodParser) comp[1]); 811 } 812 } 813 814 private static Object [] createComposite(List elementPairs) { 815 switch (elementPairs.size()) { 816 case 0: 817 return new Object [] {Literal.EMPTY, Literal.EMPTY}; 818 case 1: 819 return new Object [] {elementPairs.get(0), elementPairs.get(1)}; 820 default: 821 Composite comp = new Composite(elementPairs); 822 return new Object [] {comp, comp}; 823 } 824 } 825 826 831 static interface PeriodFieldAffix { 832 int calculatePrintedLength(int value); 833 834 void printTo(StringBuffer buf, int value); 835 836 void printTo(Writer out, int value) throws IOException ; 837 838 841 int parse(String periodStr, int position); 842 843 846 int scan(String periodStr, int position); 847 } 848 849 853 static class SimpleAffix implements PeriodFieldAffix { 854 private final String iText; 855 856 SimpleAffix(String text) { 857 iText = text; 858 } 859 860 public int calculatePrintedLength(int value) { 861 return iText.length(); 862 } 863 864 public void printTo(StringBuffer buf, int value) { 865 buf.append(iText); 866 } 867 868 public void printTo(Writer out, int value) throws IOException { 869 out.write(iText); 870 } 871 872 public int parse(String periodStr, int position) { 873 String text = iText; 874 int textLength = text.length(); 875 if (periodStr.regionMatches(true, position, text, 0, textLength)) { 876 return position + textLength; 877 } 878 return ~position; 879 } 880 881 public int scan(String periodStr, final int position) { 882 String text = iText; 883 int textLength = text.length(); 884 int sourceLength = periodStr.length(); 885 search: 886 for (int pos = position; pos < sourceLength; pos++) { 887 if (periodStr.regionMatches(true, pos, text, 0, textLength)) { 888 return pos; 889 } 890 switch (periodStr.charAt(pos)) { 892 case '0': case '1': case '2': case '3': case '4': 893 case '5': case '6': case '7': case '8': case '9': 894 case '.': case ',': case '+': case '-': 895 break; 896 default: 897 break search; 898 } 899 } 900 return ~position; 901 } 902 } 903 904 909 static class PluralAffix implements PeriodFieldAffix { 910 private final String iSingularText; 911 private final String iPluralText; 912 913 PluralAffix(String singularText, String pluralText) { 914 iSingularText = singularText; 915 iPluralText = pluralText; 916 } 917 918 public int calculatePrintedLength(int value) { 919 return (value == 1 ? iSingularText : iPluralText).length(); 920 } 921 922 public void printTo(StringBuffer buf, int value) { 923 buf.append(value == 1 ? iSingularText : iPluralText); 924 } 925 926 public void printTo(Writer out, int value) throws IOException { 927 out.write(value == 1 ? iSingularText : iPluralText); 928 } 929 930 public int parse(String periodStr, int position) { 931 String text1 = iPluralText; 932 String text2 = iSingularText; 933 934 if (text1.length() < text2.length()) { 935 String temp = text1; 937 text1 = text2; 938 text2 = temp; 939 } 940 941 if (periodStr.regionMatches 942 (true, position, text1, 0, text1.length())) { 943 return position + text1.length(); 944 } 945 if (periodStr.regionMatches 946 (true, position, text2, 0, text2.length())) { 947 return position + text2.length(); 948 } 949 950 return ~position; 951 } 952 953 public int scan(String periodStr, final int position) { 954 String text1 = iPluralText; 955 String text2 = iSingularText; 956 957 if (text1.length() < text2.length()) { 958 String temp = text1; 960 text1 = text2; 961 text2 = temp; 962 } 963 964 int textLength1 = text1.length(); 965 int textLength2 = text2.length(); 966 967 int sourceLength = periodStr.length(); 968 for (int pos = position; pos < sourceLength; pos++) { 969 if (periodStr.regionMatches(true, pos, text1, 0, textLength1)) { 970 return pos; 971 } 972 if (periodStr.regionMatches(true, pos, text2, 0, textLength2)) { 973 return pos; 974 } 975 } 976 return ~position; 977 } 978 } 979 980 984 static class CompositeAffix implements PeriodFieldAffix { 985 private final PeriodFieldAffix iLeft; 986 private final PeriodFieldAffix iRight; 987 988 CompositeAffix(PeriodFieldAffix left, PeriodFieldAffix right) { 989 iLeft = left; 990 iRight = right; 991 } 992 993 public int calculatePrintedLength(int value) { 994 return iLeft.calculatePrintedLength(value) 995 + iRight.calculatePrintedLength(value); 996 } 997 998 public void printTo(StringBuffer buf, int value) { 999 iLeft.printTo(buf, value); 1000 iRight.printTo(buf, value); 1001 } 1002 1003 public void printTo(Writer out, int value) throws IOException { 1004 iLeft.printTo(out, value); 1005 iRight.printTo(out, value); 1006 } 1007 1008 public int parse(String periodStr, int position) { 1009 position = iLeft.parse(periodStr, position); 1010 if (position >= 0) { 1011 position = iRight.parse(periodStr, position); 1012 } 1013 return position; 1014 } 1015 1016 public int scan(String periodStr, final int position) { 1017 int pos = iLeft.scan(periodStr, position); 1018 if (pos >= 0) { 1019 return iRight.scan(periodStr, pos); 1020 } 1021 return ~position; 1022 } 1023 } 1024 1025 1029 static class FieldFormatter 1030 implements PeriodPrinter, PeriodParser { 1031 private final int iMinPrintedDigits; 1032 private final int iPrintZeroSetting; 1033 private final int iMaxParsedDigits; 1034 private final boolean iRejectSignedValues; 1035 1036 1037 private final int iFieldType; 1038 1042 private final FieldFormatter[] iFieldFormatters; 1043 1044 private final PeriodFieldAffix iPrefix; 1045 private final PeriodFieldAffix iSuffix; 1046 1047 FieldFormatter(int minPrintedDigits, int printZeroSetting, 1048 int maxParsedDigits, boolean rejectSignedValues, 1049 int fieldType, FieldFormatter[] fieldFormatters, 1050 PeriodFieldAffix prefix, PeriodFieldAffix suffix) { 1051 iMinPrintedDigits = minPrintedDigits; 1052 iPrintZeroSetting = printZeroSetting; 1053 iMaxParsedDigits = maxParsedDigits; 1054 iRejectSignedValues = rejectSignedValues; 1055 iFieldType = fieldType; 1056 iFieldFormatters = fieldFormatters; 1057 iPrefix = prefix; 1058 iSuffix = suffix; 1059 } 1060 1061 FieldFormatter(FieldFormatter field, PeriodFieldAffix suffix) { 1062 iMinPrintedDigits = field.iMinPrintedDigits; 1063 iPrintZeroSetting = field.iPrintZeroSetting; 1064 iMaxParsedDigits = field.iMaxParsedDigits; 1065 iRejectSignedValues = field.iRejectSignedValues; 1066 iFieldType = field.iFieldType; 1067 iFieldFormatters = field.iFieldFormatters; 1068 iPrefix = field.iPrefix; 1069 if (field.iSuffix != null) { 1070 suffix = new CompositeAffix(field.iSuffix, suffix); 1071 } 1072 iSuffix = suffix; 1073 } 1074 1075 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) { 1076 if (stopAt <= 0) { 1077 return 0; 1078 } 1079 if (iPrintZeroSetting == PRINT_ZERO_ALWAYS || getFieldValue(period) != Long.MAX_VALUE) { 1080 return 1; 1081 } 1082 return 0; 1083 } 1084 1085 public int calculatePrintedLength(ReadablePeriod period, Locale locale) { 1086 long valueLong = getFieldValue(period); 1087 if (valueLong == Long.MAX_VALUE) { 1088 return 0; 1089 } 1090 1091 int sum = Math.max(FormatUtils.calculateDigitCount(valueLong), iMinPrintedDigits); 1092 if (iFieldType >= SECONDS_MILLIS) { 1093 sum++; if (iFieldType == SECONDS_OPTIONAL_MILLIS && 1095 (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND) == 0) { 1096 sum -= 4; } 1098 valueLong = valueLong / DateTimeConstants.MILLIS_PER_SECOND; 1099 } 1100 int value = (int) valueLong; 1101 1102 if (iPrefix != null) { 1103 sum += iPrefix.calculatePrintedLength(value); 1104 } 1105 if (iSuffix != null) { 1106 sum += iSuffix.calculatePrintedLength(value); 1107 } 1108 1109 return sum; 1110 } 1111 1112 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) { 1113 long valueLong = getFieldValue(period); 1114 if (valueLong == Long.MAX_VALUE) { 1115 return; 1116 } 1117 int value = (int) valueLong; 1118 if (iFieldType >= SECONDS_MILLIS) { 1119 value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND); 1120 } 1121 1122 if (iPrefix != null) { 1123 iPrefix.printTo(buf, value); 1124 } 1125 int minDigits = iMinPrintedDigits; 1126 if (minDigits <= 1) { 1127 FormatUtils.appendUnpaddedInteger(buf, value); 1128 } else { 1129 FormatUtils.appendPaddedInteger(buf, value, minDigits); 1130 } 1131 if (iFieldType >= SECONDS_MILLIS) { 1132 int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND); 1133 if (iFieldType == SECONDS_MILLIS || dp > 0) { 1134 buf.append('.'); 1135 FormatUtils.appendPaddedInteger(buf, dp, 3); 1136 } 1137 } 1138 if (iSuffix != null) { 1139 iSuffix.printTo(buf, value); 1140 } 1141 } 1142 1143 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException { 1144 long valueLong = getFieldValue(period); 1145 if (valueLong == Long.MAX_VALUE) { 1146 return; 1147 } 1148 int value = (int) valueLong; 1149 if (iFieldType >= SECONDS_MILLIS) { 1150 value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND); 1151 } 1152 1153 if (iPrefix != null) { 1154 iPrefix.printTo(out, value); 1155 } 1156 int minDigits = iMinPrintedDigits; 1157 if (minDigits <= 1) { 1158 FormatUtils.writeUnpaddedInteger(out, value); 1159 } else { 1160 FormatUtils.writePaddedInteger(out, value, minDigits); 1161 } 1162 if (iFieldType >= SECONDS_MILLIS) { 1163 int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND); 1164 if (iFieldType == SECONDS_MILLIS || dp > 0) { 1165 out.write('.'); 1166 FormatUtils.writePaddedInteger(out, dp, 3); 1167 } 1168 } 1169 if (iSuffix != null) { 1170 iSuffix.printTo(out, value); 1171 } 1172 } 1173 1174 public int parseInto( 1175 ReadWritablePeriod period, String text, 1176 int position, Locale locale) { 1177 1178 boolean mustParse = (iPrintZeroSetting == PRINT_ZERO_ALWAYS); 1179 1180 if (position >= text.length()) { 1182 return mustParse ? ~position : position; 1183 } 1184 1185 if (iPrefix != null) { 1186 position = iPrefix.parse(text, position); 1187 if (position >= 0) { 1188 mustParse = true; 1190 } else { 1191 if (!mustParse) { 1193 return ~position; 1197 } 1198 return position; 1199 } 1200 } 1201 1202 int suffixPos = -1; 1203 if (iSuffix != null && !mustParse) { 1204 suffixPos = iSuffix.scan(text, position); 1207 if (suffixPos >= 0) { 1208 mustParse = true; 1210 } else { 1211 if (!mustParse) { 1213 return ~suffixPos; 1217 } 1218 return suffixPos; 1219 } 1220 } 1221 1222 if (!mustParse && !isSupported(period.getPeriodType(), iFieldType)) { 1223 return position; 1226 } 1227 1228 int limit; 1229 if (suffixPos > 0) { 1230 limit = Math.min(iMaxParsedDigits, suffixPos - position); 1231 } else { 1232 limit = Math.min(iMaxParsedDigits, text.length() - position); 1233 } 1234 1235 int length = 0; 1237 int fractPos = -1; 1238 boolean hasDigits = false; 1239 while (length < limit) { 1240 char c = text.charAt(position + length); 1241 if (length == 0 && (c == '-' || c == '+') && !iRejectSignedValues) { 1243 boolean negative = c == '-'; 1244 1245 if (length + 1 >= limit || 1247 (c = text.charAt(position + length + 1)) < '0' || c > '9') 1248 { 1249 break; 1250 } 1251 1252 if (negative) { 1253 length++; 1254 } else { 1255 position++; 1257 } 1258 limit = Math.min(limit + 1, text.length() - position); 1260 continue; 1261 } 1262 if (c >= '0' && c <= '9') { 1264 hasDigits = true; 1265 } else { 1266 if ((c == '.' || c == ',') 1267 && (iFieldType == SECONDS_MILLIS || iFieldType == SECONDS_OPTIONAL_MILLIS)) { 1268 if (fractPos >= 0) { 1269 break; 1271 } 1272 fractPos = position + length + 1; 1273 limit = Math.min(limit + 1, text.length() - position); 1275 } else { 1276 break; 1277 } 1278 } 1279 length++; 1280 } 1281 1282 if (!hasDigits) { 1283 return ~position; 1284 } 1285 1286 if (suffixPos >= 0 && position + length != suffixPos) { 1287 return position; 1292 } 1293 1294 if (iFieldType != SECONDS_MILLIS && iFieldType != SECONDS_OPTIONAL_MILLIS) { 1295 setFieldValue(period, iFieldType, parseInt(text, position, length)); 1297 } else if (fractPos < 0) { 1298 setFieldValue(period, SECONDS, parseInt(text, position, length)); 1299 setFieldValue(period, MILLIS, 0); 1300 } else { 1301 int wholeValue = parseInt(text, position, fractPos - position - 1); 1302 setFieldValue(period, SECONDS, wholeValue); 1303 1304 int fractLen = position + length - fractPos; 1305 int fractValue; 1306 if (fractLen <= 0) { 1307 fractValue = 0; 1308 } else { 1309 if (fractLen >= 3) { 1310 fractValue = parseInt(text, fractPos, 3); 1311 } else { 1312 fractValue = parseInt(text, fractPos, fractLen); 1313 if (fractLen == 1) { 1314 fractValue *= 100; 1315 } else { 1316 fractValue *= 10; 1317 } 1318 } 1319 if (wholeValue < 0) { 1320 fractValue = -fractValue; 1321 } 1322 } 1323 1324 setFieldValue(period, MILLIS, fractValue); 1325 } 1326 1327 position += length; 1328 1329 if (position >= 0 && iSuffix != null) { 1330 position = iSuffix.parse(text, position); 1331 } 1332 1333 return position; 1334 } 1335 1336 1342 private int parseInt(String text, int position, int length) { 1343 if (length >= 10) { 1344 return Integer.parseInt(text.substring(position, position + length)); 1346 } 1347 if (length <= 0) { 1348 return 0; 1349 } 1350 int value = text.charAt(position++); 1351 length--; 1352 boolean negative; 1353 if (value == '-') { 1354 if (--length < 0) { 1355 return 0; 1356 } 1357 negative = true; 1358 value = text.charAt(position++); 1359 } else { 1360 negative = false; 1361 } 1362 value -= '0'; 1363 while (length-- > 0) { 1364 value = ((value << 3) + (value << 1)) + text.charAt(position++) - '0'; 1365 } 1366 return negative ? -value : value; 1367 } 1368 1369 1372 long getFieldValue(ReadablePeriod period) { 1373 PeriodType type; 1374 if (iPrintZeroSetting == PRINT_ZERO_ALWAYS) { 1375 type = null; } else { 1377 type = period.getPeriodType(); 1378 } 1379 if (type != null && isSupported(type, iFieldType) == false) { 1380 return Long.MAX_VALUE; 1381 } 1382 1383 long value; 1384 1385 switch (iFieldType) { 1386 default: 1387 return Long.MAX_VALUE; 1388 case YEARS: 1389 value = period.get(DurationFieldType.years()); 1390 break; 1391 case MONTHS: 1392 value = period.get(DurationFieldType.months()); 1393 break; 1394 case WEEKS: 1395 value = period.get(DurationFieldType.weeks()); 1396 break; 1397 case DAYS: 1398 value = period.get(DurationFieldType.days()); 1399 break; 1400 case HOURS: 1401 value = period.get(DurationFieldType.hours()); 1402 break; 1403 case MINUTES: 1404 value = period.get(DurationFieldType.minutes()); 1405 break; 1406 case SECONDS: 1407 value = period.get(DurationFieldType.seconds()); 1408 break; 1409 case MILLIS: 1410 value = period.get(DurationFieldType.millis()); 1411 break; 1412 case SECONDS_MILLIS: case SECONDS_OPTIONAL_MILLIS: 1414 int seconds = period.get(DurationFieldType.seconds()); 1415 int millis = period.get(DurationFieldType.millis()); 1416 value = (seconds * (long) DateTimeConstants.MILLIS_PER_SECOND) + millis; 1417 break; 1418 } 1419 1420 if (value == 0) { 1422 switch (iPrintZeroSetting) { 1423 case PRINT_ZERO_NEVER: 1424 return Long.MAX_VALUE; 1425 case PRINT_ZERO_RARELY_LAST: 1426 if (isZero(period) && iFieldFormatters[iFieldType] == this) { 1427 for (int i = iFieldType + 1; i < 10; i++) { 1428 if (isSupported(type, i) && iFieldFormatters[i] != null) { 1429 return Long.MAX_VALUE; 1430 } 1431 } 1432 } else { 1433 return Long.MAX_VALUE; 1434 } 1435 break; 1436 case PRINT_ZERO_RARELY_FIRST: 1437 if (isZero(period) && iFieldFormatters[iFieldType] == this) { 1438 for (int i = Math.min(iFieldType, 8) - 1; i >= 0; i++) { 1439 if (isSupported(type, i) && iFieldFormatters[i] != null) { 1440 return Long.MAX_VALUE; 1441 } 1442 } 1443 } else { 1444 return Long.MAX_VALUE; 1445 } 1446 break; 1447 } 1448 } 1449 1450 return value; 1451 } 1452 1453 boolean isZero(ReadablePeriod period) { 1454 for (int i = 0, isize = period.size(); i < isize; i++) { 1455 if (period.getValue(i) != 0) { 1456 return false; 1457 } 1458 } 1459 return true; 1460 } 1461 1462 boolean isSupported(PeriodType type, int field) { 1463 switch (field) { 1464 default: 1465 return false; 1466 case YEARS: 1467 return type.isSupported(DurationFieldType.years()); 1468 case MONTHS: 1469 return type.isSupported(DurationFieldType.months()); 1470 case WEEKS: 1471 return type.isSupported(DurationFieldType.weeks()); 1472 case DAYS: 1473 return type.isSupported(DurationFieldType.days()); 1474 case HOURS: 1475 return type.isSupported(DurationFieldType.hours()); 1476 case MINUTES: 1477 return type.isSupported(DurationFieldType.minutes()); 1478 case SECONDS: 1479 return type.isSupported(DurationFieldType.seconds()); 1480 case MILLIS: 1481 return type.isSupported(DurationFieldType.millis()); 1482 case SECONDS_MILLIS: case SECONDS_OPTIONAL_MILLIS: 1484 return type.isSupported(DurationFieldType.seconds()) || 1485 type.isSupported(DurationFieldType.millis()); 1486 } 1487 } 1488 1489 void setFieldValue(ReadWritablePeriod period, int field, int value) { 1490 switch (field) { 1491 default: 1492 break; 1493 case YEARS: 1494 period.setYears(value); 1495 break; 1496 case MONTHS: 1497 period.setMonths(value); 1498 break; 1499 case WEEKS: 1500 period.setWeeks(value); 1501 break; 1502 case DAYS: 1503 period.setDays(value); 1504 break; 1505 case HOURS: 1506 period.setHours(value); 1507 break; 1508 case MINUTES: 1509 period.setMinutes(value); 1510 break; 1511 case SECONDS: 1512 period.setSeconds(value); 1513 break; 1514 case MILLIS: 1515 period.setMillis(value); 1516 break; 1517 } 1518 } 1519 1520 int getFieldType() { 1521 return iFieldType; 1522 } 1523 } 1524 1525 1529 static class Literal 1530 implements PeriodPrinter, PeriodParser { 1531 static final Literal EMPTY = new Literal(""); 1532 private final String iText; 1533 1534 Literal(String text) { 1535 iText = text; 1536 } 1537 1538 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) { 1539 return 0; 1540 } 1541 1542 public int calculatePrintedLength(ReadablePeriod period, Locale locale) { 1543 return iText.length(); 1544 } 1545 1546 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) { 1547 buf.append(iText); 1548 } 1549 1550 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException { 1551 out.write(iText); 1552 } 1553 1554 public int parseInto( 1555 ReadWritablePeriod period, String periodStr, 1556 int position, Locale locale) { 1557 if (periodStr.regionMatches(true, position, iText, 0, iText.length())) { 1558 return position + iText.length(); 1559 } 1560 return ~position; 1561 } 1562 } 1563 1564 1569 static class Separator 1570 implements PeriodPrinter, PeriodParser { 1571 private final String iText; 1572 private final String iFinalText; 1573 private final String [] iParsedForms; 1574 1575 private final boolean iUseBefore; 1576 private final boolean iUseAfter; 1577 1578 private PeriodPrinter iBeforePrinter; 1579 private PeriodPrinter iAfterPrinter; 1580 private PeriodParser iBeforeParser; 1581 private PeriodParser iAfterParser; 1582 1583 Separator(String text, String finalText, String [] variants, 1584 PeriodPrinter beforePrinter, PeriodParser beforeParser, 1585 boolean useBefore, boolean useAfter) { 1586 iText = text; 1587 iFinalText = finalText; 1588 1589 if ((finalText == null || text.equals(finalText)) && 1590 (variants == null || variants.length == 0)) { 1591 1592 iParsedForms = new String [] {text}; 1593 } else { 1594 TreeSet parsedSet = new TreeSet (String.CASE_INSENSITIVE_ORDER); 1596 parsedSet.add(text); 1597 parsedSet.add(finalText); 1598 if (variants != null) { 1599 for (int i=variants.length; --i>=0; ) { 1600 parsedSet.add(variants[i]); 1601 } 1602 } 1603 ArrayList parsedList = new ArrayList (parsedSet); 1604 Collections.reverse(parsedList); 1605 iParsedForms = (String []) parsedList.toArray(new String [parsedList.size()]); 1606 } 1607 1608 iBeforePrinter = beforePrinter; 1609 iBeforeParser = beforeParser; 1610 iUseBefore = useBefore; 1611 iUseAfter = useAfter; 1612 } 1613 1614 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) { 1615 int sum = iBeforePrinter.countFieldsToPrint(period, stopAt, locale); 1616 if (sum < stopAt) { 1617 sum += iAfterPrinter.countFieldsToPrint(period, stopAt, locale); 1618 } 1619 return sum; 1620 } 1621 1622 public int calculatePrintedLength(ReadablePeriod period, Locale locale) { 1623 PeriodPrinter before = iBeforePrinter; 1624 PeriodPrinter after = iAfterPrinter; 1625 1626 int sum = before.calculatePrintedLength(period, locale) 1627 + after.calculatePrintedLength(period, locale); 1628 1629 if (iUseBefore) { 1630 if (before.countFieldsToPrint(period, 1, locale) > 0) { 1631 if (iUseAfter) { 1632 int afterCount = after.countFieldsToPrint(period, 2, locale); 1633 if (afterCount > 0) { 1634 sum += (afterCount > 1 ? iText : iFinalText).length(); 1635 } 1636 } else { 1637 sum += iText.length(); 1638 } 1639 } 1640 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) { 1641 sum += iText.length(); 1642 } 1643 1644 return sum; 1645 } 1646 1647 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) { 1648 PeriodPrinter before = iBeforePrinter; 1649 PeriodPrinter after = iAfterPrinter; 1650 1651 before.printTo(buf, period, locale); 1652 if (iUseBefore) { 1653 if (before.countFieldsToPrint(period, 1, locale) > 0) { 1654 if (iUseAfter) { 1655 int afterCount = after.countFieldsToPrint(period, 2, locale); 1656 if (afterCount > 0) { 1657 buf.append(afterCount > 1 ? iText : iFinalText); 1658 } 1659 } else { 1660 buf.append(iText); 1661 } 1662 } 1663 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) { 1664 buf.append(iText); 1665 } 1666 after.printTo(buf, period, locale); 1667 } 1668 1669 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException { 1670 PeriodPrinter before = iBeforePrinter; 1671 PeriodPrinter after = iAfterPrinter; 1672 1673 before.printTo(out, period, locale); 1674 if (iUseBefore) { 1675 if (before.countFieldsToPrint(period, 1, locale) > 0) { 1676 if (iUseAfter) { 1677 int afterCount = after.countFieldsToPrint(period, 2, locale); 1678 if (afterCount > 0) { 1679 out.write(afterCount > 1 ? iText : iFinalText); 1680 } 1681 } else { 1682 out.write(iText); 1683 } 1684 } 1685 } else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) { 1686 out.write(iText); 1687 } 1688 after.printTo(out, period, locale); 1689 } 1690 1691 public int parseInto( 1692 ReadWritablePeriod period, String periodStr, 1693 int position, Locale locale) { 1694 int oldPos = position; 1695 position = iBeforeParser.parseInto(period, periodStr, position, locale); 1696 1697 if (position < 0) { 1698 return position; 1699 } 1700 1701 boolean found = false; 1702 if (position > oldPos) { 1703 String [] parsedForms = iParsedForms; 1705 int length = parsedForms.length; 1706 for (int i=0; i < length; i++) { 1707 String parsedForm = parsedForms[i]; 1708 if ((parsedForm == null || parsedForm.length() == 0) || 1709 periodStr.regionMatches 1710 (true, position, parsedForm, 0, parsedForm.length())) { 1711 1712 position += parsedForm.length(); 1713 found = true; 1714 break; 1715 } 1716 } 1717 } 1718 1719 oldPos = position; 1720 position = iAfterParser.parseInto(period, periodStr, position, locale); 1721 1722 if (position < 0) { 1723 return position; 1724 } 1725 1726 if (found && position == oldPos) { 1727 return ~oldPos; 1729 } 1730 1731 if (position > oldPos && !found && !iUseBefore) { 1732 return ~oldPos; 1734 } 1735 1736 return position; 1737 } 1738 1739 Separator finish(PeriodPrinter afterPrinter, PeriodParser afterParser) { 1740 iAfterPrinter = afterPrinter; 1741 iAfterParser = afterParser; 1742 return this; 1743 } 1744 } 1745 1746 1750 static class Composite 1751 implements PeriodPrinter, PeriodParser { 1752 1753 private final PeriodPrinter[] iPrinters; 1754 private final PeriodParser[] iParsers; 1755 1756 Composite(List elementPairs) { 1757 List printerList = new ArrayList (); 1758 List parserList = new ArrayList (); 1759 1760 decompose(elementPairs, printerList, parserList); 1761 1762 if (printerList.size() <= 0) { 1763 iPrinters = null; 1764 } else { 1765 iPrinters = (PeriodPrinter[]) printerList.toArray( 1766 new PeriodPrinter[printerList.size()]); 1767 } 1768 1769 if (parserList.size() <= 0) { 1770 iParsers = null; 1771 } else { 1772 iParsers = (PeriodParser[]) parserList.toArray( 1773 new PeriodParser[parserList.size()]); 1774 } 1775 } 1776 1777 public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) { 1778 int sum = 0; 1779 PeriodPrinter[] printers = iPrinters; 1780 for (int i=printers.length; sum < stopAt && --i>=0; ) { 1781 sum += printers[i].countFieldsToPrint(period, Integer.MAX_VALUE, locale); 1782 } 1783 return sum; 1784 } 1785 1786 public int calculatePrintedLength(ReadablePeriod period, Locale locale) { 1787 int sum = 0; 1788 PeriodPrinter[] printers = iPrinters; 1789 for (int i=printers.length; --i>=0; ) { 1790 sum += printers[i].calculatePrintedLength(period, locale); 1791 } 1792 return sum; 1793 } 1794 1795 public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) { 1796 PeriodPrinter[] printers = iPrinters; 1797 int len = printers.length; 1798 for (int i=0; i<len; i++) { 1799 printers[i].printTo(buf, period, locale); 1800 } 1801 } 1802 1803 public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException { 1804 PeriodPrinter[] printers = iPrinters; 1805 int len = printers.length; 1806 for (int i=0; i<len; i++) { 1807 printers[i].printTo(out, period, locale); 1808 } 1809 } 1810 1811 public int parseInto( 1812 ReadWritablePeriod period, String periodStr, 1813 int position, Locale locale) { 1814 PeriodParser[] parsers = iParsers; 1815 if (parsers == null) { 1816 throw new UnsupportedOperationException (); 1817 } 1818 1819 int len = parsers.length; 1820 for (int i=0; i<len && position >= 0; i++) { 1821 position = parsers[i].parseInto(period, periodStr, position, locale); 1822 } 1823 return position; 1824 } 1825 1826 private void decompose(List elementPairs, List printerList, List parserList) { 1827 int size = elementPairs.size(); 1828 for (int i=0; i<size; i+=2) { 1829 Object element = elementPairs.get(i); 1830 if (element instanceof PeriodPrinter) { 1831 if (element instanceof Composite) { 1832 addArrayToList(printerList, ((Composite) element).iPrinters); 1833 } else { 1834 printerList.add(element); 1835 } 1836 } 1837 1838 element = elementPairs.get(i + 1); 1839 if (element instanceof PeriodParser) { 1840 if (element instanceof Composite) { 1841 addArrayToList(parserList, ((Composite) element).iParsers); 1842 } else { 1843 parserList.add(element); 1844 } 1845 } 1846 } 1847 } 1848 1849 private void addArrayToList(List list, Object [] array) { 1850 if (array != null) { 1851 for (int i=0; i<array.length; i++) { 1852 list.add(array[i]); 1853 } 1854 } 1855 } 1856 } 1857 1858} 1859 | Popular Tags |