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.List ; 22 import java.util.Locale ; 23 24 import org.joda.time.Chronology; 25 import org.joda.time.DateTimeConstants; 26 import org.joda.time.DateTimeField; 27 import org.joda.time.DateTimeFieldType; 28 import org.joda.time.DateTimeZone; 29 import org.joda.time.ReadablePartial; 30 import org.joda.time.field.MillisDurationField; 31 import org.joda.time.field.PreciseDateTimeField; 32 33 65 public class DateTimeFormatterBuilder { 66 67 68 private ArrayList iElementPairs; 69 70 private Object iFormatter; 71 72 76 public DateTimeFormatterBuilder() { 77 super(); 78 iElementPairs = new ArrayList (); 79 } 80 81 97 public DateTimeFormatter toFormatter() { 98 Object f = getFormatter(); 99 DateTimePrinter printer = null; 100 if (isPrinter(f)) { 101 printer = (DateTimePrinter) f; 102 } 103 DateTimeParser parser = null; 104 if (isParser(f)) { 105 parser = (DateTimeParser) f; 106 } 107 if (printer != null || parser != null) { 108 return new DateTimeFormatter(printer, parser); 109 } 110 throw new UnsupportedOperationException ("Both printing and parsing not supported"); 111 } 112 113 125 public DateTimePrinter toPrinter() { 126 Object f = getFormatter(); 127 if (isPrinter(f)) { 128 return (DateTimePrinter) f; 129 } 130 throw new UnsupportedOperationException ("Printing is not supported"); 131 } 132 133 145 public DateTimeParser toParser() { 146 Object f = getFormatter(); 147 if (isParser(f)) { 148 return (DateTimeParser) f; 149 } 150 throw new UnsupportedOperationException ("Parsing is not supported"); 151 } 152 153 160 public boolean canBuildFormatter() { 161 return isFormatter(getFormatter()); 162 } 163 164 170 public boolean canBuildPrinter() { 171 return isPrinter(getFormatter()); 172 } 173 174 180 public boolean canBuildParser() { 181 return isParser(getFormatter()); 182 } 183 184 189 public void clear() { 190 iFormatter = null; 191 iElementPairs.clear(); 192 } 193 194 202 public DateTimeFormatterBuilder append(DateTimeFormatter formatter) { 203 if (formatter == null) { 204 throw new IllegalArgumentException ("No formatter supplied"); 205 } 206 return append0(formatter.getPrinter(), formatter.getParser()); 207 } 208 209 217 public DateTimeFormatterBuilder append(DateTimePrinter printer) { 218 checkPrinter(printer); 219 return append0(printer, null); 220 } 221 222 230 public DateTimeFormatterBuilder append(DateTimeParser parser) { 231 checkParser(parser); 232 return append0(null, parser); 233 } 234 235 243 public DateTimeFormatterBuilder append(DateTimePrinter printer, DateTimeParser parser) { 244 checkPrinter(printer); 245 checkParser(parser); 246 return append0(printer, parser); 247 } 248 249 266 public DateTimeFormatterBuilder append(DateTimePrinter printer, DateTimeParser[] parsers) { 267 if (printer != null) { 268 checkPrinter(printer); 269 } 270 if (parsers == null) { 271 throw new IllegalArgumentException ("No parsers supplied"); 272 } 273 int length = parsers.length; 274 if (length == 1) { 275 if (parsers[0] == null) { 276 throw new IllegalArgumentException ("No parser supplied"); 277 } 278 return append0(printer, parsers[0]); 279 } 280 281 DateTimeParser[] copyOfParsers = new DateTimeParser[length]; 282 int i; 283 for (i = 0; i < length - 1; i++) { 284 if ((copyOfParsers[i] = parsers[i]) == null) { 285 throw new IllegalArgumentException ("Incomplete parser array"); 286 } 287 } 288 copyOfParsers[i] = parsers[i]; 289 290 return append0(printer, new MatchingParser(copyOfParsers)); 291 } 292 293 300 public DateTimeFormatterBuilder appendOptional(DateTimeParser parser) { 301 checkParser(parser); 302 DateTimeParser[] parsers = new DateTimeParser[] {parser, null}; 303 return append0(null, new MatchingParser(parsers)); 304 } 305 306 312 private void checkParser(DateTimeParser parser) { 313 if (parser == null) { 314 throw new IllegalArgumentException ("No parser supplied"); 315 } 316 } 317 318 323 private void checkPrinter(DateTimePrinter printer) { 324 if (printer == null) { 325 throw new IllegalArgumentException ("No printer supplied"); 326 } 327 } 328 329 private DateTimeFormatterBuilder append0(Object element) { 330 iFormatter = null; 331 iElementPairs.add(element); 333 iElementPairs.add(element); 334 return this; 335 } 336 337 private DateTimeFormatterBuilder append0( 338 DateTimePrinter printer, DateTimeParser parser) { 339 iFormatter = null; 340 iElementPairs.add(printer); 341 iElementPairs.add(parser); 342 return this; 343 } 344 345 352 public DateTimeFormatterBuilder appendLiteral(char c) { 353 return append0(new CharacterLiteral(c)); 354 } 355 356 363 public DateTimeFormatterBuilder appendLiteral(String text) { 364 if (text == null) { 365 throw new IllegalArgumentException ("Literal must not be null"); 366 } 367 switch (text.length()) { 368 case 0: 369 return this; 370 case 1: 371 return append0(new CharacterLiteral(text.charAt(0))); 372 default: 373 return append0(new StringLiteral(text)); 374 } 375 } 376 377 388 public DateTimeFormatterBuilder appendDecimal( 389 DateTimeFieldType fieldType, int minDigits, int maxDigits) { 390 if (fieldType == null) { 391 throw new IllegalArgumentException ("Field type must not be null"); 392 } 393 if (maxDigits < minDigits) { 394 maxDigits = minDigits; 395 } 396 if (minDigits < 0 || maxDigits <= 0) { 397 throw new IllegalArgumentException (); 398 } 399 if (minDigits <= 1) { 400 return append0(new UnpaddedNumber(fieldType, maxDigits, false)); 401 } else { 402 return append0(new PaddedNumber(fieldType, maxDigits, false, minDigits)); 403 } 404 } 405 406 417 public DateTimeFormatterBuilder appendSignedDecimal( 418 DateTimeFieldType fieldType, int minDigits, int maxDigits) { 419 if (fieldType == null) { 420 throw new IllegalArgumentException ("Field type must not be null"); 421 } 422 if (maxDigits < minDigits) { 423 maxDigits = minDigits; 424 } 425 if (minDigits < 0 || maxDigits <= 0) { 426 throw new IllegalArgumentException (); 427 } 428 if (minDigits <= 1) { 429 return append0(new UnpaddedNumber(fieldType, maxDigits, true)); 430 } else { 431 return append0(new PaddedNumber(fieldType, maxDigits, true, minDigits)); 432 } 433 } 434 435 443 public DateTimeFormatterBuilder appendText(DateTimeFieldType fieldType) { 444 if (fieldType == null) { 445 throw new IllegalArgumentException ("Field type must not be null"); 446 } 447 return append0(new TextField(fieldType, false)); 448 } 449 450 458 public DateTimeFormatterBuilder appendShortText(DateTimeFieldType fieldType) { 459 if (fieldType == null) { 460 throw new IllegalArgumentException ("Field type must not be null"); 461 } 462 return append0(new TextField(fieldType, true)); 463 } 464 465 478 public DateTimeFormatterBuilder appendFraction( 479 DateTimeFieldType fieldType, int minDigits, int maxDigits) { 480 if (fieldType == null) { 481 throw new IllegalArgumentException ("Field type must not be null"); 482 } 483 if (maxDigits < minDigits) { 484 maxDigits = minDigits; 485 } 486 if (minDigits < 0 || maxDigits <= 0) { 487 throw new IllegalArgumentException (); 488 } 489 return append0(new Fraction(fieldType, minDigits, maxDigits)); 490 } 491 492 497 public DateTimeFormatterBuilder appendFractionOfSecond(int minDigits, int maxDigits) { 498 return appendFraction(DateTimeFieldType.secondOfDay(), minDigits, maxDigits); 499 } 500 501 506 public DateTimeFormatterBuilder appendFractionOfMinute(int minDigits, int maxDigits) { 507 return appendFraction(DateTimeFieldType.minuteOfDay(), minDigits, maxDigits); 508 } 509 510 515 public DateTimeFormatterBuilder appendFractionOfHour(int minDigits, int maxDigits) { 516 return appendFraction(DateTimeFieldType.hourOfDay(), minDigits, maxDigits); 517 } 518 519 524 public DateTimeFormatterBuilder appendFractionOfDay(int minDigits, int maxDigits) { 525 return appendFraction(DateTimeFieldType.dayOfYear(), minDigits, maxDigits); 526 } 527 528 534 public DateTimeFormatterBuilder appendMillisOfSecond(int minDigits) { 535 return appendDecimal(DateTimeFieldType.millisOfSecond(), minDigits, 3); 536 } 537 538 544 public DateTimeFormatterBuilder appendMillisOfDay(int minDigits) { 545 return appendDecimal(DateTimeFieldType.millisOfDay(), minDigits, 8); 546 } 547 548 554 public DateTimeFormatterBuilder appendSecondOfMinute(int minDigits) { 555 return appendDecimal(DateTimeFieldType.secondOfMinute(), minDigits, 2); 556 } 557 558 564 public DateTimeFormatterBuilder appendSecondOfDay(int minDigits) { 565 return appendDecimal(DateTimeFieldType.secondOfDay(), minDigits, 5); 566 } 567 568 574 public DateTimeFormatterBuilder appendMinuteOfHour(int minDigits) { 575 return appendDecimal(DateTimeFieldType.minuteOfHour(), minDigits, 2); 576 } 577 578 584 public DateTimeFormatterBuilder appendMinuteOfDay(int minDigits) { 585 return appendDecimal(DateTimeFieldType.minuteOfDay(), minDigits, 4); 586 } 587 588 594 public DateTimeFormatterBuilder appendHourOfDay(int minDigits) { 595 return appendDecimal(DateTimeFieldType.hourOfDay(), minDigits, 2); 596 } 597 598 604 public DateTimeFormatterBuilder appendClockhourOfDay(int minDigits) { 605 return appendDecimal(DateTimeFieldType.clockhourOfDay(), minDigits, 2); 606 } 607 608 614 public DateTimeFormatterBuilder appendHourOfHalfday(int minDigits) { 615 return appendDecimal(DateTimeFieldType.hourOfHalfday(), minDigits, 2); 616 } 617 618 624 public DateTimeFormatterBuilder appendClockhourOfHalfday(int minDigits) { 625 return appendDecimal(DateTimeFieldType.clockhourOfHalfday(), minDigits, 2); 626 } 627 628 634 public DateTimeFormatterBuilder appendDayOfWeek(int minDigits) { 635 return appendDecimal(DateTimeFieldType.dayOfWeek(), minDigits, 1); 636 } 637 638 644 public DateTimeFormatterBuilder appendDayOfMonth(int minDigits) { 645 return appendDecimal(DateTimeFieldType.dayOfMonth(), minDigits, 2); 646 } 647 648 654 public DateTimeFormatterBuilder appendDayOfYear(int minDigits) { 655 return appendDecimal(DateTimeFieldType.dayOfYear(), minDigits, 3); 656 } 657 658 664 public DateTimeFormatterBuilder appendWeekOfWeekyear(int minDigits) { 665 return appendDecimal(DateTimeFieldType.weekOfWeekyear(), minDigits, 2); 666 } 667 668 676 public DateTimeFormatterBuilder appendWeekyear(int minDigits, int maxDigits) { 677 return appendSignedDecimal(DateTimeFieldType.weekyear(), minDigits, maxDigits); 678 } 679 680 686 public DateTimeFormatterBuilder appendMonthOfYear(int minDigits) { 687 return appendDecimal(DateTimeFieldType.monthOfYear(), minDigits, 2); 688 } 689 690 698 public DateTimeFormatterBuilder appendYear(int minDigits, int maxDigits) { 699 return appendSignedDecimal(DateTimeFieldType.year(), minDigits, maxDigits); 700 } 701 702 720 public DateTimeFormatterBuilder appendTwoDigitYear(int pivot) { 721 return appendTwoDigitYear(pivot, false); 722 } 723 724 738 public DateTimeFormatterBuilder appendTwoDigitYear(int pivot, boolean lenientParse) { 739 return append0(new TwoDigitYear(DateTimeFieldType.year(), pivot, lenientParse)); 740 } 741 742 760 public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot) { 761 return appendTwoDigitWeekyear(pivot, false); 762 } 763 764 778 public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot, boolean lenientParse) { 779 return append0(new TwoDigitYear(DateTimeFieldType.weekyear(), pivot, lenientParse)); 780 } 781 782 790 public DateTimeFormatterBuilder appendYearOfEra(int minDigits, int maxDigits) { 791 return appendDecimal(DateTimeFieldType.yearOfEra(), minDigits, maxDigits); 792 } 793 794 802 public DateTimeFormatterBuilder appendYearOfCentury(int minDigits, int maxDigits) { 803 return appendDecimal(DateTimeFieldType.yearOfCentury(), minDigits, maxDigits); 804 } 805 806 814 public DateTimeFormatterBuilder appendCenturyOfEra(int minDigits, int maxDigits) { 815 return appendSignedDecimal(DateTimeFieldType.centuryOfEra(), minDigits, maxDigits); 816 } 817 818 824 public DateTimeFormatterBuilder appendHalfdayOfDayText() { 825 return appendText(DateTimeFieldType.halfdayOfDay()); 826 } 827 828 834 public DateTimeFormatterBuilder appendDayOfWeekText() { 835 return appendText(DateTimeFieldType.dayOfWeek()); 836 } 837 838 845 public DateTimeFormatterBuilder appendDayOfWeekShortText() { 846 return appendShortText(DateTimeFieldType.dayOfWeek()); 847 } 848 849 856 public DateTimeFormatterBuilder appendMonthOfYearText() { 857 return appendText(DateTimeFieldType.monthOfYear()); 858 } 859 860 866 public DateTimeFormatterBuilder appendMonthOfYearShortText() { 867 return appendShortText(DateTimeFieldType.monthOfYear()); 868 } 869 870 876 public DateTimeFormatterBuilder appendEraText() { 877 return appendText(DateTimeFieldType.era()); 878 } 879 880 887 public DateTimeFormatterBuilder appendTimeZoneName() { 888 return append0(new TimeZoneName(TimeZoneName.LONG_NAME), null); 889 } 890 891 898 public DateTimeFormatterBuilder appendTimeZoneShortName() { 899 return append0(new TimeZoneName(TimeZoneName.SHORT_NAME), null); 900 } 901 902 908 public DateTimeFormatterBuilder appendTimeZoneId() { 909 return append0(new TimeZoneName(TimeZoneName.ID), null); 910 } 911 912 926 public DateTimeFormatterBuilder appendTimeZoneOffset( 927 String zeroOffsetText, boolean showSeparators, 928 int minFields, int maxFields) { 929 return append0(new TimeZoneOffset 930 (zeroOffsetText, showSeparators, minFields, maxFields)); 931 } 932 933 942 public DateTimeFormatterBuilder appendPattern(String pattern) { 943 DateTimeFormat.appendPatternTo(this, pattern); 944 return this; 945 } 946 947 private Object getFormatter() { 949 Object f = iFormatter; 950 951 if (f == null) { 952 if (iElementPairs.size() == 2) { 953 Object printer = iElementPairs.get(0); 954 Object parser = iElementPairs.get(1); 955 956 if (printer != null) { 957 if (printer == parser || parser == null) { 958 f = printer; 959 } 960 } else { 961 f = parser; 962 } 963 } 964 965 if (f == null) { 966 f = new Composite(iElementPairs); 967 } 968 969 iFormatter = f; 970 } 971 972 return f; 973 } 974 975 private boolean isPrinter(Object f) { 976 if (f instanceof DateTimePrinter) { 977 if (f instanceof Composite) { 978 return ((Composite)f).isPrinter(); 979 } 980 return true; 981 } 982 return false; 983 } 984 985 private boolean isParser(Object f) { 986 if (f instanceof DateTimeParser) { 987 if (f instanceof Composite) { 988 return ((Composite)f).isParser(); 989 } 990 return true; 991 } 992 return false; 993 } 994 995 private boolean isFormatter(Object f) { 996 return (isPrinter(f) || isParser(f)); 997 } 998 999 static void appendUnknownString(StringBuffer buf, int len) { 1000 for (int i = len; --i >= 0;) { 1001 buf.append('\ufffd'); 1002 } 1003 } 1004 1005 static void printUnknownString(Writer out, int len) throws IOException { 1006 for (int i = len; --i >= 0;) { 1007 out.write('\ufffd'); 1008 } 1009 } 1010 1011 static class CharacterLiteral 1013 implements DateTimePrinter, DateTimeParser { 1014 1015 private final char iValue; 1016 1017 CharacterLiteral(char value) { 1018 super(); 1019 iValue = value; 1020 } 1021 1022 public int estimatePrintedLength() { 1023 return 1; 1024 } 1025 1026 public void printTo( 1027 StringBuffer buf, long instant, Chronology chrono, 1028 int displayOffset, DateTimeZone displayZone, Locale locale) { 1029 buf.append(iValue); 1030 } 1031 1032 public void printTo( 1033 Writer out, long instant, Chronology chrono, 1034 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1035 out.write(iValue); 1036 } 1037 1038 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1039 buf.append(iValue); 1040 } 1041 1042 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1043 out.write(iValue); 1044 } 1045 1046 public int estimateParsedLength() { 1047 return 1; 1048 } 1049 1050 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1051 if (position >= text.length()) { 1052 return ~position; 1053 } 1054 1055 char a = text.charAt(position); 1056 char b = iValue; 1057 1058 if (a != b) { 1059 a = Character.toUpperCase(a); 1060 b = Character.toUpperCase(b); 1061 if (a != b) { 1062 a = Character.toLowerCase(a); 1063 b = Character.toLowerCase(b); 1064 if (a != b) { 1065 return ~position; 1066 } 1067 } 1068 } 1069 1070 return position + 1; 1071 } 1072 } 1073 1074 static class StringLiteral 1076 implements DateTimePrinter, DateTimeParser { 1077 1078 private final String iValue; 1079 1080 StringLiteral(String value) { 1081 super(); 1082 iValue = value; 1083 } 1084 1085 public int estimatePrintedLength() { 1086 return iValue.length(); 1087 } 1088 1089 public void printTo( 1090 StringBuffer buf, long instant, Chronology chrono, 1091 int displayOffset, DateTimeZone displayZone, Locale locale) { 1092 buf.append(iValue); 1093 } 1094 1095 public void printTo( 1096 Writer out, long instant, Chronology chrono, 1097 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1098 out.write(iValue); 1099 } 1100 1101 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1102 buf.append(iValue); 1103 } 1104 1105 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1106 out.write(iValue); 1107 } 1108 1109 public int estimateParsedLength() { 1110 return iValue.length(); 1111 } 1112 1113 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1114 if (text.regionMatches(true, position, iValue, 0, iValue.length())) { 1115 return position + iValue.length(); 1116 } 1117 return ~position; 1118 } 1119 } 1120 1121 static abstract class NumberFormatter 1123 implements DateTimePrinter, DateTimeParser { 1124 protected final DateTimeFieldType iFieldType; 1125 protected final int iMaxParsedDigits; 1126 protected final boolean iSigned; 1127 1128 NumberFormatter(DateTimeFieldType fieldType, 1129 int maxParsedDigits, boolean signed) { 1130 super(); 1131 iFieldType = fieldType; 1132 iMaxParsedDigits = maxParsedDigits; 1133 iSigned = signed; 1134 } 1135 1136 public int estimateParsedLength() { 1137 return iMaxParsedDigits; 1138 } 1139 1140 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1141 int limit = Math.min(iMaxParsedDigits, text.length() - position); 1142 1143 boolean negative = false; 1144 int length = 0; 1145 while (length < limit) { 1146 char c = text.charAt(position + length); 1147 if (length == 0 && (c == '-' || c == '+') && iSigned) { 1148 negative = c == '-'; 1149 1150 if (length + 1 >= limit || 1152 (c = text.charAt(position + length + 1)) < '0' || c > '9') 1153 { 1154 break; 1155 } 1156 1157 if (negative) { 1158 length++; 1159 } else { 1160 position++; 1162 } 1163 limit = Math.min(limit + 1, text.length() - position); 1165 continue; 1166 } 1167 if (c < '0' || c > '9') { 1168 break; 1169 } 1170 length++; 1171 } 1172 1173 if (length == 0) { 1174 return ~position; 1175 } 1176 1177 int value; 1178 if (length >= 9) { 1179 value = Integer.parseInt(text.substring(position, position += length)); 1182 } else { 1183 int i = position; 1184 if (negative) { 1185 i++; 1186 } 1187 try { 1188 value = text.charAt(i++) - '0'; 1189 } catch (StringIndexOutOfBoundsException e) { 1190 return ~position; 1191 } 1192 position += length; 1193 while (i < position) { 1194 value = ((value << 3) + (value << 1)) + text.charAt(i++) - '0'; 1195 } 1196 if (negative) { 1197 value = -value; 1198 } 1199 } 1200 1201 bucket.saveField(iFieldType, value); 1202 return position; 1203 } 1204 } 1205 1206 static class UnpaddedNumber extends NumberFormatter { 1208 1209 protected UnpaddedNumber(DateTimeFieldType fieldType, 1210 int maxParsedDigits, boolean signed) 1211 { 1212 super(fieldType, maxParsedDigits, signed); 1213 } 1214 1215 public int estimatePrintedLength() { 1216 return iMaxParsedDigits; 1217 } 1218 1219 public void printTo( 1220 StringBuffer buf, long instant, Chronology chrono, 1221 int displayOffset, DateTimeZone displayZone, Locale locale) { 1222 try { 1223 DateTimeField field = iFieldType.getField(chrono); 1224 FormatUtils.appendUnpaddedInteger(buf, field.get(instant)); 1225 } catch (RuntimeException e) { 1226 buf.append('\ufffd'); 1227 } 1228 } 1229 1230 public void printTo( 1231 Writer out, long instant, Chronology chrono, 1232 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1233 try { 1234 DateTimeField field = iFieldType.getField(chrono); 1235 FormatUtils.writeUnpaddedInteger(out, field.get(instant)); 1236 } catch (RuntimeException e) { 1237 out.write('\ufffd'); 1238 } 1239 } 1240 1241 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1242 if (partial.isSupported(iFieldType)) { 1243 try { 1244 FormatUtils.appendUnpaddedInteger(buf, partial.get(iFieldType)); 1245 } catch (RuntimeException e) { 1246 buf.append('\ufffd'); 1247 } 1248 } else { 1249 buf.append('\ufffd'); 1250 } 1251 } 1252 1253 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1254 if (partial.isSupported(iFieldType)) { 1255 try { 1256 FormatUtils.writeUnpaddedInteger(out, partial.get(iFieldType)); 1257 } catch (RuntimeException e) { 1258 out.write('\ufffd'); 1259 } 1260 } else { 1261 out.write('\ufffd'); 1262 } 1263 } 1264 } 1265 1266 static class PaddedNumber extends NumberFormatter { 1268 1269 protected final int iMinPrintedDigits; 1270 1271 protected PaddedNumber(DateTimeFieldType fieldType, int maxParsedDigits, 1272 boolean signed, int minPrintedDigits) 1273 { 1274 super(fieldType, maxParsedDigits, signed); 1275 iMinPrintedDigits = minPrintedDigits; 1276 } 1277 1278 public int estimatePrintedLength() { 1279 return iMaxParsedDigits; 1280 } 1281 1282 public void printTo( 1283 StringBuffer buf, long instant, Chronology chrono, 1284 int displayOffset, DateTimeZone displayZone, Locale locale) { 1285 try { 1286 DateTimeField field = iFieldType.getField(chrono); 1287 FormatUtils.appendPaddedInteger(buf, field.get(instant), iMinPrintedDigits); 1288 } catch (RuntimeException e) { 1289 appendUnknownString(buf, iMinPrintedDigits); 1290 } 1291 } 1292 1293 public void printTo( 1294 Writer out, long instant, Chronology chrono, 1295 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1296 try { 1297 DateTimeField field = iFieldType.getField(chrono); 1298 FormatUtils.writePaddedInteger(out, field.get(instant), iMinPrintedDigits); 1299 } catch (RuntimeException e) { 1300 printUnknownString(out, iMinPrintedDigits); 1301 } 1302 } 1303 1304 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1305 if (partial.isSupported(iFieldType)) { 1306 try { 1307 FormatUtils.appendPaddedInteger(buf, partial.get(iFieldType), iMinPrintedDigits); 1308 } catch (RuntimeException e) { 1309 appendUnknownString(buf, iMinPrintedDigits); 1310 } 1311 } else { 1312 appendUnknownString(buf, iMinPrintedDigits); 1313 } 1314 } 1315 1316 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1317 if (partial.isSupported(iFieldType)) { 1318 try { 1319 FormatUtils.writePaddedInteger(out, partial.get(iFieldType), iMinPrintedDigits); 1320 } catch (RuntimeException e) { 1321 printUnknownString(out, iMinPrintedDigits); 1322 } 1323 } else { 1324 printUnknownString(out, iMinPrintedDigits); 1325 } 1326 } 1327 } 1328 1329 static class TwoDigitYear 1331 implements DateTimePrinter, DateTimeParser { 1332 1333 1334 private final DateTimeFieldType iType; 1335 1336 private final int iPivot; 1337 private final boolean iLenientParse; 1338 1339 TwoDigitYear(DateTimeFieldType type, int pivot, boolean lenientParse) { 1340 super(); 1341 iType = type; 1342 iPivot = pivot; 1343 iLenientParse = lenientParse; 1344 } 1345 1346 public int estimateParsedLength() { 1347 return iLenientParse ? 4 : 2; 1348 } 1349 1350 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1351 int limit = text.length() - position; 1352 1353 if (!iLenientParse) { 1354 limit = Math.min(2, limit); 1355 if (limit < 2) { 1356 return ~position; 1357 } 1358 } else { 1359 boolean hasSignChar = false; 1360 boolean negative = false; 1361 int length = 0; 1362 while (length < limit) { 1363 char c = text.charAt(position + length); 1364 if (length == 0 && (c == '-' || c == '+')) { 1365 hasSignChar = true; 1366 negative = c == '-'; 1367 if (negative) { 1368 length++; 1369 } else { 1370 position++; 1372 limit--; 1373 } 1374 continue; 1375 } 1376 if (c < '0' || c > '9') { 1377 break; 1378 } 1379 length++; 1380 } 1381 1382 if (length == 0) { 1383 return ~position; 1384 } 1385 1386 if (hasSignChar || length != 2) { 1387 int value; 1388 if (length >= 9) { 1389 value = Integer.parseInt(text.substring(position, position += length)); 1392 } else { 1393 int i = position; 1394 if (negative) { 1395 i++; 1396 } 1397 try { 1398 value = text.charAt(i++) - '0'; 1399 } catch (StringIndexOutOfBoundsException e) { 1400 return ~position; 1401 } 1402 position += length; 1403 while (i < position) { 1404 value = ((value << 3) + (value << 1)) + text.charAt(i++) - '0'; 1405 } 1406 if (negative) { 1407 value = -value; 1408 } 1409 } 1410 1411 bucket.saveField(iType, value); 1412 return position; 1413 } 1414 } 1415 1416 int year; 1417 char c = text.charAt(position); 1418 if (c < '0' || c > '9') { 1419 return ~position; 1420 } 1421 year = c - '0'; 1422 c = text.charAt(position + 1); 1423 if (c < '0' || c > '9') { 1424 return ~position; 1425 } 1426 year = ((year << 3) + (year << 1)) + c - '0'; 1427 1428 int pivot = iPivot; 1429 if (bucket.getPivotYear() != null) { 1431 pivot = bucket.getPivotYear().intValue(); 1432 } 1433 1434 int low = pivot - 50; 1435 1436 int t; 1437 if (low >= 0) { 1438 t = low % 100; 1439 } else { 1440 t = 99 + ((low + 1) % 100); 1441 } 1442 1443 year += low + ((year < t) ? 100 : 0) - t; 1444 1445 bucket.saveField(iType, year); 1446 return position + 2; 1447 } 1448 1449 public int estimatePrintedLength() { 1450 return 2; 1451 } 1452 1453 public void printTo( 1454 StringBuffer buf, long instant, Chronology chrono, 1455 int displayOffset, DateTimeZone displayZone, Locale locale) { 1456 int year = getTwoDigitYear(instant, chrono); 1457 if (year < 0) { 1458 buf.append('\ufffd'); 1459 buf.append('\ufffd'); 1460 } else { 1461 FormatUtils.appendPaddedInteger(buf, year, 2); 1462 } 1463 } 1464 1465 public void printTo( 1466 Writer out, long instant, Chronology chrono, 1467 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1468 int year = getTwoDigitYear(instant, chrono); 1469 if (year < 0) { 1470 out.write('\ufffd'); 1471 out.write('\ufffd'); 1472 } else { 1473 FormatUtils.writePaddedInteger(out, year, 2); 1474 } 1475 } 1476 1477 private int getTwoDigitYear(long instant, Chronology chrono) { 1478 try { 1479 int year = iType.getField(chrono).get(instant); 1480 if (year < 0) { 1481 year = -year; 1482 } 1483 return year % 100; 1484 } catch (RuntimeException e) { 1485 return -1; 1486 } 1487 } 1488 1489 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1490 int year = getTwoDigitYear(partial); 1491 if (year < 0) { 1492 buf.append('\ufffd'); 1493 buf.append('\ufffd'); 1494 } else { 1495 FormatUtils.appendPaddedInteger(buf, year, 2); 1496 } 1497 } 1498 1499 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1500 int year = getTwoDigitYear(partial); 1501 if (year < 0) { 1502 out.write('\ufffd'); 1503 out.write('\ufffd'); 1504 } else { 1505 FormatUtils.writePaddedInteger(out, year, 2); 1506 } 1507 } 1508 1509 private int getTwoDigitYear(ReadablePartial partial) { 1510 if (partial.isSupported(iType)) { 1511 try { 1512 int year = partial.get(iType); 1513 if (year < 0) { 1514 year = -year; 1515 } 1516 return year % 100; 1517 } catch (RuntimeException e) {} 1518 } 1519 return -1; 1520 } 1521 } 1522 1523 static class TextField 1525 implements DateTimePrinter, DateTimeParser { 1526 1527 private final DateTimeFieldType iFieldType; 1528 private final boolean iShort; 1529 1530 TextField(DateTimeFieldType fieldType, boolean isShort) { 1531 super(); 1532 iFieldType = fieldType; 1533 iShort = isShort; 1534 } 1535 1536 public int estimatePrintedLength() { 1537 return iShort ? 6 : 20; 1538 } 1539 1540 public void printTo( 1541 StringBuffer buf, long instant, Chronology chrono, 1542 int displayOffset, DateTimeZone displayZone, Locale locale) { 1543 try { 1544 buf.append(print(instant, chrono, locale)); 1545 } catch (RuntimeException e) { 1546 buf.append('\ufffd'); 1547 } 1548 } 1549 1550 public void printTo( 1551 Writer out, long instant, Chronology chrono, 1552 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1553 try { 1554 out.write(print(instant, chrono, locale)); 1555 } catch (RuntimeException e) { 1556 out.write('\ufffd'); 1557 } 1558 } 1559 1560 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1561 try { 1562 buf.append(print(partial, locale)); 1563 } catch (RuntimeException e) { 1564 buf.append('\ufffd'); 1565 } 1566 } 1567 1568 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1569 try { 1570 out.write(print(partial, locale)); 1571 } catch (RuntimeException e) { 1572 out.write('\ufffd'); 1573 } 1574 } 1575 1576 private String print(long instant, Chronology chrono, Locale locale) { 1577 DateTimeField field = iFieldType.getField(chrono); 1578 if (iShort) { 1579 return field.getAsShortText(instant, locale); 1580 } else { 1581 return field.getAsText(instant, locale); 1582 } 1583 } 1584 1585 private String print(ReadablePartial partial, Locale locale) { 1586 if (partial.isSupported(iFieldType)) { 1587 DateTimeField field = iFieldType.getField(partial.getChronology()); 1588 if (iShort) { 1589 return field.getAsShortText(partial, locale); 1590 } else { 1591 return field.getAsText(partial, locale); 1592 } 1593 } else { 1594 return "\ufffd"; 1595 } 1596 } 1597 1598 public int estimateParsedLength() { 1599 return estimatePrintedLength(); 1600 } 1601 1602 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1603 int limit = text.length(); 1604 int i = position; 1605 for (; i<limit; i++) { 1606 char c = text.charAt(i); 1607 if (c < 'A') { 1608 break; 1609 } 1610 if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || Character.isLetter(c)) { 1611 continue; 1612 } 1613 break; 1614 } 1615 1616 if (i == position) { 1617 return ~position; 1618 } 1619 1620 Locale locale = bucket.getLocale(); 1621 bucket.saveField(iFieldType, text.substring(position, i), locale); 1622 1623 return i; 1624 } 1625 } 1626 1627 static class Fraction 1629 implements DateTimePrinter, DateTimeParser { 1630 1631 private final DateTimeFieldType iFieldType; 1632 protected int iMinDigits; 1633 protected int iMaxDigits; 1634 1635 protected Fraction(DateTimeFieldType fieldType, int minDigits, int maxDigits) { 1636 super(); 1637 iFieldType = fieldType; 1638 if (maxDigits > 18) { 1640 maxDigits = 18; 1641 } 1642 iMinDigits = minDigits; 1643 iMaxDigits = maxDigits; 1644 } 1645 1646 public int estimatePrintedLength() { 1647 return iMaxDigits; 1648 } 1649 1650 public void printTo( 1651 StringBuffer buf, long instant, Chronology chrono, 1652 int displayOffset, DateTimeZone displayZone, Locale locale) { 1653 try { 1654 printTo(buf, null, instant, chrono); 1655 } catch (IOException e) { 1656 } 1658 } 1659 1660 public void printTo( 1661 Writer out, long instant, Chronology chrono, 1662 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1663 printTo(null, out, instant, chrono); 1664 } 1665 1666 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 1667 if (partial.isSupported(iFieldType)) { 1668 long millis = partial.getChronology().set(partial, 0L); 1669 try { 1670 printTo(buf, null, millis, partial.getChronology()); 1671 } catch (IOException e) { 1672 } 1674 } else { 1675 buf.append('\ufffd'); 1676 } 1677 } 1678 1679 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 1680 if (partial.isSupported(iFieldType)) { 1681 long millis = partial.getChronology().set(partial, 0L); 1682 printTo(null, out, millis, partial.getChronology()); 1683 } else { 1684 out.write('\ufffd'); 1685 } 1686 } 1687 1688 protected void printTo(StringBuffer buf, Writer out, long instant, Chronology chrono) 1689 throws IOException 1690 { 1691 DateTimeField field = iFieldType.getField(chrono); 1692 int minDigits = iMinDigits; 1693 1694 long fraction; 1695 try { 1696 fraction = field.remainder(instant); 1697 } catch (RuntimeException e) { 1698 if (buf != null) { 1699 appendUnknownString(buf, minDigits); 1700 } else { 1701 printUnknownString(out, minDigits); 1702 } 1703 return; 1704 } 1705 1706 if (fraction == 0) { 1707 if (buf != null) { 1708 while (--minDigits >= 0) { 1709 buf.append('0'); 1710 } 1711 } else { 1712 while (--minDigits >= 0) { 1713 out.write('0'); 1714 } 1715 } 1716 return; 1717 } 1718 1719 String str; 1720 long[] fractionData = getFractionData(fraction, field); 1721 long scaled = fractionData[0]; 1722 int maxDigits = (int) fractionData[1]; 1723 1724 if ((scaled & 0x7fffffff) == scaled) { 1725 str = Integer.toString((int) scaled); 1726 } else { 1727 str = Long.toString(scaled); 1728 } 1729 1730 int length = str.length(); 1731 int digits = maxDigits; 1732 while (length < digits) { 1733 if (buf != null) { 1734 buf.append('0'); 1735 } else { 1736 out.write('0'); 1737 } 1738 minDigits--; 1739 digits--; 1740 } 1741 1742 if (minDigits < digits) { 1743 while (minDigits < digits) { 1745 if (length <= 1 || str.charAt(length - 1) != '0') { 1746 break; 1747 } 1748 digits--; 1749 length--; 1750 } 1751 if (length < str.length()) { 1752 if (buf != null) { 1753 for (int i=0; i<length; i++) { 1754 buf.append(str.charAt(i)); 1755 } 1756 } else { 1757 for (int i=0; i<length; i++) { 1758 out.write(str.charAt(i)); 1759 } 1760 } 1761 return; 1762 } 1763 } 1764 1765 if (buf != null) { 1766 buf.append(str); 1767 } else { 1768 out.write(str); 1769 } 1770 } 1771 1772 private long[] getFractionData(long fraction, DateTimeField field) { 1773 long rangeMillis = field.getDurationField().getUnitMillis(); 1774 long scalar; 1775 int maxDigits = iMaxDigits; 1776 while (true) { 1777 switch (maxDigits) { 1778 default: scalar = 1L; break; 1779 case 1: scalar = 10L; break; 1780 case 2: scalar = 100L; break; 1781 case 3: scalar = 1000L; break; 1782 case 4: scalar = 10000L; break; 1783 case 5: scalar = 100000L; break; 1784 case 6: scalar = 1000000L; break; 1785 case 7: scalar = 10000000L; break; 1786 case 8: scalar = 100000000L; break; 1787 case 9: scalar = 1000000000L; break; 1788 case 10: scalar = 10000000000L; break; 1789 case 11: scalar = 100000000000L; break; 1790 case 12: scalar = 1000000000000L; break; 1791 case 13: scalar = 10000000000000L; break; 1792 case 14: scalar = 100000000000000L; break; 1793 case 15: scalar = 1000000000000000L; break; 1794 case 16: scalar = 10000000000000000L; break; 1795 case 17: scalar = 100000000000000000L; break; 1796 case 18: scalar = 1000000000000000000L; break; 1797 } 1798 if (((rangeMillis * scalar) / scalar) == rangeMillis) { 1799 break; 1800 } 1801 maxDigits--; 1803 } 1804 1805 return new long[] {fraction * scalar / rangeMillis, maxDigits}; 1806 } 1807 1808 public int estimateParsedLength() { 1809 return iMaxDigits; 1810 } 1811 1812 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 1813 DateTimeField field = iFieldType.getField(bucket.getChronology()); 1814 1815 int limit = Math.min(iMaxDigits, text.length() - position); 1816 1817 long value = 0; 1818 long n = field.getDurationField().getUnitMillis() * 10; 1819 int length = 0; 1820 while (length < limit) { 1821 char c = text.charAt(position + length); 1822 if (c < '0' || c > '9') { 1823 break; 1824 } 1825 length++; 1826 long nn = n / 10; 1827 value += (c - '0') * nn; 1828 n = nn; 1829 } 1830 1831 value /= 10; 1832 1833 if (length == 0) { 1834 return ~position; 1835 } 1836 1837 if (value > Integer.MAX_VALUE) { 1838 return ~position; 1839 } 1840 1841 DateTimeField parseField = new PreciseDateTimeField( 1842 DateTimeFieldType.millisOfSecond(), 1843 MillisDurationField.INSTANCE, 1844 field.getDurationField()); 1845 1846 bucket.saveField(parseField, (int) value); 1847 1848 return position + length; 1849 } 1850 } 1851 1852 static class TimeZoneOffset 1854 implements DateTimePrinter, DateTimeParser { 1855 1856 private final String iZeroOffsetText; 1857 private final boolean iShowSeparators; 1858 private final int iMinFields; 1859 private final int iMaxFields; 1860 1861 TimeZoneOffset(String zeroOffsetText, 1862 boolean showSeparators, 1863 int minFields, int maxFields) 1864 { 1865 super(); 1866 iZeroOffsetText = zeroOffsetText; 1867 iShowSeparators = showSeparators; 1868 if (minFields <= 0 || maxFields < minFields) { 1869 throw new IllegalArgumentException (); 1870 } 1871 if (minFields > 4) { 1872 minFields = 4; 1873 maxFields = 4; 1874 } 1875 iMinFields = minFields; 1876 iMaxFields = maxFields; 1877 } 1878 1879 public int estimatePrintedLength() { 1880 int est = 1 + iMinFields << 1; 1881 if (iShowSeparators) { 1882 est += iMinFields - 1; 1883 } 1884 if (iZeroOffsetText != null && iZeroOffsetText.length() > est) { 1885 est = iZeroOffsetText.length(); 1886 } 1887 return est; 1888 } 1889 1890 public void printTo( 1891 StringBuffer buf, long instant, Chronology chrono, 1892 int displayOffset, DateTimeZone displayZone, Locale locale) { 1893 if (displayZone == null) { 1894 return; } 1896 if (displayOffset == 0 && iZeroOffsetText != null) { 1897 buf.append(iZeroOffsetText); 1898 return; 1899 } 1900 if (displayOffset >= 0) { 1901 buf.append('+'); 1902 } else { 1903 buf.append('-'); 1904 displayOffset = -displayOffset; 1905 } 1906 1907 int hours = displayOffset / DateTimeConstants.MILLIS_PER_HOUR; 1908 FormatUtils.appendPaddedInteger(buf, hours, 2); 1909 if (iMaxFields == 1) { 1910 return; 1911 } 1912 displayOffset -= hours * (int)DateTimeConstants.MILLIS_PER_HOUR; 1913 if (displayOffset == 0 && iMinFields <= 1) { 1914 return; 1915 } 1916 1917 int minutes = displayOffset / DateTimeConstants.MILLIS_PER_MINUTE; 1918 if (iShowSeparators) { 1919 buf.append(':'); 1920 } 1921 FormatUtils.appendPaddedInteger(buf, minutes, 2); 1922 if (iMaxFields == 2) { 1923 return; 1924 } 1925 displayOffset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE; 1926 if (displayOffset == 0 && iMinFields <= 2) { 1927 return; 1928 } 1929 1930 int seconds = displayOffset / DateTimeConstants.MILLIS_PER_SECOND; 1931 if (iShowSeparators) { 1932 buf.append(':'); 1933 } 1934 FormatUtils.appendPaddedInteger(buf, seconds, 2); 1935 if (iMaxFields == 3) { 1936 return; 1937 } 1938 displayOffset -= seconds * DateTimeConstants.MILLIS_PER_SECOND; 1939 if (displayOffset == 0 && iMinFields <= 3) { 1940 return; 1941 } 1942 1943 if (iShowSeparators) { 1944 buf.append('.'); 1945 } 1946 FormatUtils.appendPaddedInteger(buf, displayOffset, 3); 1947 } 1948 1949 public void printTo( 1950 Writer out, long instant, Chronology chrono, 1951 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 1952 if (displayZone == null) { 1953 return; } 1955 if (displayOffset == 0 && iZeroOffsetText != null) { 1956 out.write(iZeroOffsetText); 1957 return; 1958 } 1959 if (displayOffset >= 0) { 1960 out.write('+'); 1961 } else { 1962 out.write('-'); 1963 displayOffset = -displayOffset; 1964 } 1965 1966 int hours = displayOffset / DateTimeConstants.MILLIS_PER_HOUR; 1967 FormatUtils.writePaddedInteger(out, hours, 2); 1968 if (iMaxFields == 1) { 1969 return; 1970 } 1971 displayOffset -= hours * (int)DateTimeConstants.MILLIS_PER_HOUR; 1972 if (displayOffset == 0 && iMinFields == 1) { 1973 return; 1974 } 1975 1976 int minutes = displayOffset / DateTimeConstants.MILLIS_PER_MINUTE; 1977 if (iShowSeparators) { 1978 out.write(':'); 1979 } 1980 FormatUtils.writePaddedInteger(out, minutes, 2); 1981 if (iMaxFields == 2) { 1982 return; 1983 } 1984 displayOffset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE; 1985 if (displayOffset == 0 && iMinFields == 2) { 1986 return; 1987 } 1988 1989 int seconds = displayOffset / DateTimeConstants.MILLIS_PER_SECOND; 1990 if (iShowSeparators) { 1991 out.write(':'); 1992 } 1993 FormatUtils.writePaddedInteger(out, seconds, 2); 1994 if (iMaxFields == 3) { 1995 return; 1996 } 1997 displayOffset -= seconds * DateTimeConstants.MILLIS_PER_SECOND; 1998 if (displayOffset == 0 && iMinFields == 3) { 1999 return; 2000 } 2001 2002 if (iShowSeparators) { 2003 out.write('.'); 2004 } 2005 FormatUtils.writePaddedInteger(out, displayOffset, 3); 2006 } 2007 2008 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 2009 } 2011 2012 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 2013 } 2015 2016 public int estimateParsedLength() { 2017 return estimatePrintedLength(); 2018 } 2019 2020 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 2021 int limit = text.length() - position; 2022 2023 zeroOffset: 2024 if (iZeroOffsetText != null) { 2025 if (iZeroOffsetText.length() == 0) { 2026 if (limit > 0) { 2028 char c = text.charAt(position); 2029 if (c == '-' || c == '+') { 2030 break zeroOffset; 2031 } 2032 } 2033 bucket.setOffset(0); 2034 return position; 2035 } 2036 if (text.regionMatches(true, position, iZeroOffsetText, 0, 2037 iZeroOffsetText.length())) { 2038 bucket.setOffset(0); 2039 return position + iZeroOffsetText.length(); 2040 } 2041 } 2042 2043 2045 if (limit <= 1) { 2046 return ~position; 2047 } 2048 2049 boolean negative; 2050 char c = text.charAt(position); 2051 if (c == '-') { 2052 negative = true; 2053 } else if (c == '+') { 2054 negative = false; 2055 } else { 2056 return ~position; 2057 } 2058 2059 limit--; 2060 position++; 2061 2062 2072 2074 if (digitCount(text, position, 2) < 2) { 2075 return ~position; 2077 } 2078 2079 int offset; 2080 2081 int hours = FormatUtils.parseTwoDigits(text, position); 2082 if (hours > 23) { 2083 return ~position; 2084 } 2085 offset = hours * DateTimeConstants.MILLIS_PER_HOUR; 2086 limit -= 2; 2087 position += 2; 2088 2089 parse: { 2090 2093 if (limit <= 0) { 2094 break parse; 2095 } 2096 2097 boolean expectSeparators; 2098 c = text.charAt(position); 2099 if (c == ':') { 2100 expectSeparators = true; 2101 limit--; 2102 position++; 2103 } else if (c >= '0' && c <= '9') { 2104 expectSeparators = false; 2105 } else { 2106 break parse; 2107 } 2108 2109 2111 int count = digitCount(text, position, 2); 2112 if (count == 0 && !expectSeparators) { 2113 break parse; 2114 } else if (count < 2) { 2115 return ~position; 2117 } 2118 2119 int minutes = FormatUtils.parseTwoDigits(text, position); 2120 if (minutes > 59) { 2121 return ~position; 2122 } 2123 offset += minutes * DateTimeConstants.MILLIS_PER_MINUTE; 2124 limit -= 2; 2125 position += 2; 2126 2127 2129 if (limit <= 0) { 2130 break parse; 2131 } 2132 2133 if (expectSeparators) { 2134 if (text.charAt(position) != ':') { 2135 break parse; 2136 } 2137 limit--; 2138 position++; 2139 } 2140 2141 count = digitCount(text, position, 2); 2142 if (count == 0 && !expectSeparators) { 2143 break parse; 2144 } else if (count < 2) { 2145 return ~position; 2147 } 2148 2149 int seconds = FormatUtils.parseTwoDigits(text, position); 2150 if (seconds > 59) { 2151 return ~position; 2152 } 2153 offset += seconds * DateTimeConstants.MILLIS_PER_SECOND; 2154 limit -= 2; 2155 position += 2; 2156 2157 2159 if (limit <= 0) { 2160 break parse; 2161 } 2162 2163 if (expectSeparators) { 2164 if (text.charAt(position) != '.' && text.charAt(position) != ',') { 2165 break parse; 2166 } 2167 limit--; 2168 position++; 2169 } 2170 2171 count = digitCount(text, position, 3); 2172 if (count == 0 && !expectSeparators) { 2173 break parse; 2174 } else if (count < 1) { 2175 return ~position; 2177 } 2178 2179 offset += (text.charAt(position++) - '0') * 100; 2180 if (count > 1) { 2181 offset += (text.charAt(position++) - '0') * 10; 2182 if (count > 2) { 2183 offset += text.charAt(position++) - '0'; 2184 } 2185 } 2186 } 2187 2188 bucket.setOffset(negative ? -offset : offset); 2189 return position; 2190 } 2191 2192 2196 private int digitCount(String text, int position, int amount) { 2197 int limit = Math.min(text.length() - position, amount); 2198 amount = 0; 2199 for (; limit > 0; limit--) { 2200 char c = text.charAt(position + amount); 2201 if (c < '0' || c > '9') { 2202 break; 2203 } 2204 amount++; 2205 } 2206 return amount; 2207 } 2208 } 2209 2210 static class TimeZoneName 2212 implements DateTimePrinter { 2213 2214 static final int LONG_NAME = 0; 2215 static final int SHORT_NAME = 1; 2216 static final int ID = 2; 2217 2218 private final int iType; 2219 2220 TimeZoneName(int type) { 2221 super(); 2222 iType = type; 2223 } 2224 2225 public int estimatePrintedLength() { 2226 return (iType == SHORT_NAME ? 4 : 20); 2227 } 2228 2229 public void printTo( 2230 StringBuffer buf, long instant, Chronology chrono, 2231 int displayOffset, DateTimeZone displayZone, Locale locale) { 2232 buf.append(print(instant - displayOffset, displayZone, locale)); 2233 } 2234 2235 public void printTo( 2236 Writer out, long instant, Chronology chrono, 2237 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 2238 out.write(print(instant - displayOffset, displayZone, locale)); 2239 } 2240 2241 private String print(long instant, DateTimeZone displayZone, Locale locale) { 2242 if (displayZone == null) { 2243 return ""; } 2245 switch (iType) { 2246 case LONG_NAME: 2247 return displayZone.getName(instant, locale); 2248 case SHORT_NAME: 2249 return displayZone.getShortName(instant, locale); 2250 case ID: 2251 return displayZone.getID(); 2252 } 2253 return ""; 2254 } 2255 2256 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 2257 } 2259 2260 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 2261 } 2263 } 2264 2265 static class Composite 2267 implements DateTimePrinter, DateTimeParser { 2268 2269 private final DateTimePrinter[] iPrinters; 2270 private final DateTimeParser[] iParsers; 2271 2272 private final int iPrintedLengthEstimate; 2273 private final int iParsedLengthEstimate; 2274 2275 Composite(List elementPairs) { 2276 super(); 2277 2278 List printerList = new ArrayList (); 2279 List parserList = new ArrayList (); 2280 2281 decompose(elementPairs, printerList, parserList); 2282 2283 if (printerList.size() <= 0) { 2284 iPrinters = null; 2285 iPrintedLengthEstimate = 0; 2286 } else { 2287 int size = printerList.size(); 2288 iPrinters = new DateTimePrinter[size]; 2289 int printEst = 0; 2290 for (int i=0; i<size; i++) { 2291 DateTimePrinter printer = (DateTimePrinter) printerList.get(i); 2292 printEst += printer.estimatePrintedLength(); 2293 iPrinters[i] = printer; 2294 } 2295 iPrintedLengthEstimate = printEst; 2296 } 2297 2298 if (parserList.size() <= 0) { 2299 iParsers = null; 2300 iParsedLengthEstimate = 0; 2301 } else { 2302 int size = parserList.size(); 2303 iParsers = new DateTimeParser[size]; 2304 int parseEst = 0; 2305 for (int i=0; i<size; i++) { 2306 DateTimeParser parser = (DateTimeParser) parserList.get(i); 2307 parseEst += parser.estimateParsedLength(); 2308 iParsers[i] = parser; 2309 } 2310 iParsedLengthEstimate = parseEst; 2311 } 2312 } 2313 2314 public int estimatePrintedLength() { 2315 return iPrintedLengthEstimate; 2316 } 2317 2318 public void printTo( 2319 StringBuffer buf, long instant, Chronology chrono, 2320 int displayOffset, DateTimeZone displayZone, Locale locale) { 2321 DateTimePrinter[] elements = iPrinters; 2322 if (elements == null) { 2323 throw new UnsupportedOperationException (); 2324 } 2325 2326 int len = elements.length; 2327 for (int i = 0; i < len; i++) { 2328 elements[i].printTo(buf, instant, chrono, displayOffset, displayZone, locale); 2329 } 2330 } 2331 2332 public void printTo( 2333 Writer out, long instant, Chronology chrono, 2334 int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException { 2335 DateTimePrinter[] elements = iPrinters; 2336 if (elements == null) { 2337 throw new UnsupportedOperationException (); 2338 } 2339 2340 int len = elements.length; 2341 for (int i = 0; i < len; i++) { 2342 elements[i].printTo(out, instant, chrono, displayOffset, displayZone, locale); 2343 } 2344 } 2345 2346 public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { 2347 DateTimePrinter[] elements = iPrinters; 2348 if (elements == null) { 2349 throw new UnsupportedOperationException (); 2350 } 2351 2352 int len = elements.length; 2353 for (int i=0; i<len; i++) { 2354 elements[i].printTo(buf, partial, locale); 2355 } 2356 } 2357 2358 public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { 2359 DateTimePrinter[] elements = iPrinters; 2360 if (elements == null) { 2361 throw new UnsupportedOperationException (); 2362 } 2363 2364 int len = elements.length; 2365 for (int i=0; i<len; i++) { 2366 elements[i].printTo(out, partial, locale); 2367 } 2368 } 2369 2370 public int estimateParsedLength() { 2371 return iParsedLengthEstimate; 2372 } 2373 2374 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 2375 DateTimeParser[] elements = iParsers; 2376 if (elements == null) { 2377 throw new UnsupportedOperationException (); 2378 } 2379 2380 int len = elements.length; 2381 for (int i=0; i<len && position >= 0; i++) { 2382 position = elements[i].parseInto(bucket, text, position); 2383 } 2384 return position; 2385 } 2386 2387 boolean isPrinter() { 2388 return iPrinters != null; 2389 } 2390 2391 boolean isParser() { 2392 return iParsers != null; 2393 } 2394 2395 2399 private void decompose(List elementPairs, List printerList, List parserList) { 2400 int size = elementPairs.size(); 2401 for (int i=0; i<size; i+=2) { 2402 Object element = elementPairs.get(i); 2403 if (element instanceof DateTimePrinter) { 2404 if (element instanceof Composite) { 2405 addArrayToList(printerList, ((Composite)element).iPrinters); 2406 } else { 2407 printerList.add(element); 2408 } 2409 } 2410 2411 element = elementPairs.get(i + 1); 2412 if (element instanceof DateTimeParser) { 2413 if (element instanceof Composite) { 2414 addArrayToList(parserList, ((Composite)element).iParsers); 2415 } else { 2416 parserList.add(element); 2417 } 2418 } 2419 } 2420 } 2421 2422 private void addArrayToList(List list, Object [] array) { 2423 if (array != null) { 2424 for (int i=0; i<array.length; i++) { 2425 list.add(array[i]); 2426 } 2427 } 2428 } 2429 } 2430 2431 static class MatchingParser 2433 implements DateTimeParser { 2434 2435 private final DateTimeParser[] iParsers; 2436 private final int iParsedLengthEstimate; 2437 2438 MatchingParser(DateTimeParser[] parsers) { 2439 super(); 2440 iParsers = parsers; 2441 int est = 0; 2442 for (int i=parsers.length; --i>=0 ;) { 2443 DateTimeParser parser = parsers[i]; 2444 if (parser != null) { 2445 int len = parser.estimateParsedLength(); 2446 if (len > est) { 2447 est = len; 2448 } 2449 } 2450 } 2451 iParsedLengthEstimate = est; 2452 } 2453 2454 public int estimateParsedLength() { 2455 return iParsedLengthEstimate; 2456 } 2457 2458 public int parseInto(DateTimeParserBucket bucket, String text, int position) { 2459 DateTimeParser[] parsers = iParsers; 2460 int length = parsers.length; 2461 2462 final Object originalState = bucket.saveState(); 2463 boolean isOptional = false; 2464 2465 int bestValidPos = position; 2466 Object bestValidState = null; 2467 2468 int bestInvalidPos = position; 2469 2470 for (int i=0; i<length; i++) { 2471 DateTimeParser parser = parsers[i]; 2472 if (parser == null) { 2473 if (bestValidPos <= position) { 2475 return position; 2476 } 2477 isOptional = true; 2478 break; 2479 } 2480 int parsePos = parser.parseInto(bucket, text, position); 2481 if (parsePos >= position) { 2482 if (parsePos > bestValidPos) { 2483 if (parsePos >= text.length() || 2484 (i + 1) >= length || parsers[i + 1] == null) { 2485 2486 return parsePos; 2489 } 2490 bestValidPos = parsePos; 2491 bestValidState = bucket.saveState(); 2492 } 2493 } else { 2494 if (parsePos < 0) { 2495 parsePos = ~parsePos; 2496 if (parsePos > bestInvalidPos) { 2497 bestInvalidPos = parsePos; 2498 } 2499 } 2500 } 2501 bucket.restoreState(originalState); 2502 } 2503 2504 if (bestValidPos > position || (bestValidPos == position && isOptional)) { 2505 if (bestValidState != null) { 2507 bucket.restoreState(bestValidState); 2508 } 2509 return bestValidPos; 2510 } 2511 2512 return ~bestInvalidPos; 2513 } 2514 } 2515 2516} 2517 | Popular Tags |