1
40
41 package javax.xml.bind;
42
43 import java.math.BigDecimal;
44 import java.math.BigInteger;
45 import java.util.Calendar;
46 import java.util.GregorianCalendar;
47 import java.util.TimeZone;
48
49 import javax.xml.namespace.QName;
50 import javax.xml.namespace.NamespaceContext;
51 import javax.xml.datatype.DatatypeFactory;
52 import javax.xml.datatype.DatatypeConfigurationException;
53
54
69 final class DatatypeConverterImpl implements DatatypeConverterInterface {
70
71
74 public static final DatatypeConverterInterface theInstance = new DatatypeConverterImpl();
75
76 protected DatatypeConverterImpl() {
77 }
78
79 public String parseString(String lexicalXSDString) {
80 return lexicalXSDString;
81 }
82
83 public BigInteger parseInteger(String lexicalXSDInteger) {
84 return _parseInteger(lexicalXSDInteger);
85 }
86
87 public static BigInteger _parseInteger(CharSequence s) {
88 return new BigInteger(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
89 }
90
91 public String printInteger(BigInteger val) {
92 return _printInteger(val);
93 }
94
95 public static String _printInteger(BigInteger val) {
96 return val.toString();
97 }
98
99 public int parseInt(String s) {
100 return _parseInt(s);
101 }
102
103
113 public static int _parseInt(CharSequence s) {
114 int len = s.length();
115 int sign = 1;
116
117 int r = 0;
118
119 for (int i = 0; i < len; i++) {
120 char ch = s.charAt(i);
121 if (WhiteSpaceProcessor.isWhiteSpace(ch)) {
122
123 } else if ('0' <= ch && ch <= '9') {
124 r = r * 10 + (ch - '0');
125 } else if (ch == '-') {
126 sign = -1;
127 } else if (ch == '+') {
128
129 } else {
130 throw new NumberFormatException("Not a number: " + s);
131 }
132 }
133
134 return r * sign;
135 }
136
137 public long parseLong(String lexicalXSLong) {
138 return _parseLong(lexicalXSLong);
139 }
140
141 public static long _parseLong(CharSequence s) {
142 return Long.parseLong(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
143 }
144
145 public short parseShort(String lexicalXSDShort) {
146 return _parseShort(lexicalXSDShort);
147 }
148
149 public static short _parseShort(CharSequence s) {
150 return (short) _parseInt(s);
151 }
152
153 public String printShort(short val) {
154 return _printShort(val);
155 }
156
157 public static String _printShort(short val) {
158 return String.valueOf(val);
159 }
160
161 public BigDecimal parseDecimal(String content) {
162 return _parseDecimal(content);
163 }
164
165 public static BigDecimal _parseDecimal(CharSequence content) {
166 content = WhiteSpaceProcessor.trim(content);
167
168 if (content.length() <= 0) {
169 return null;
170 }
171
172 return new BigDecimal(content.toString());
173
174
175
176
177
178
179
180
181
182
183
184
185 }
186
187 public float parseFloat(String lexicalXSDFloat) {
188 return _parseFloat(lexicalXSDFloat);
189 }
190
191 public static float _parseFloat(CharSequence _val) {
192 String s = WhiteSpaceProcessor.trim(_val).toString();
193
207
208 if (s.equals("NaN")) {
209 return Float.NaN;
210 }
211 if (s.equals("INF")) {
212 return Float.POSITIVE_INFINITY;
213 }
214 if (s.equals("-INF")) {
215 return Float.NEGATIVE_INFINITY;
216 }
217
218 if (s.length() == 0
219 || !isDigitOrPeriodOrSign(s.charAt(0))
220 || !isDigitOrPeriodOrSign(s.charAt(s.length() - 1))) {
221 throw new NumberFormatException();
222 }
223
224
225 return Float.parseFloat(s);
226 }
227
228 public String printFloat(float v) {
229 return _printFloat(v);
230 }
231
232 public static String _printFloat(float v) {
233 if (Float.isNaN(v)) {
234 return "NaN";
235 }
236 if (v == Float.POSITIVE_INFINITY) {
237 return "INF";
238 }
239 if (v == Float.NEGATIVE_INFINITY) {
240 return "-INF";
241 }
242 return String.valueOf(v);
243 }
244
245 public double parseDouble(String lexicalXSDDouble) {
246 return _parseDouble(lexicalXSDDouble);
247 }
248
249 public static double _parseDouble(CharSequence _val) {
250 String val = WhiteSpaceProcessor.trim(_val).toString();
251
252 if (val.equals("NaN")) {
253 return Double.NaN;
254 }
255 if (val.equals("INF")) {
256 return Double.POSITIVE_INFINITY;
257 }
258 if (val.equals("-INF")) {
259 return Double.NEGATIVE_INFINITY;
260 }
261
262 if (val.length() == 0
263 || !isDigitOrPeriodOrSign(val.charAt(0))
264 || !isDigitOrPeriodOrSign(val.charAt(val.length() - 1))) {
265 throw new NumberFormatException(val);
266 }
267
268
269
270 return Double.parseDouble(val);
271 }
272
273 public boolean parseBoolean(String lexicalXSDBoolean) {
274 Boolean b = _parseBoolean(lexicalXSDBoolean);
275 return (b == null) ? false : b.booleanValue();
276 }
277
278 public static Boolean _parseBoolean(CharSequence literal) {
279 if (literal == null) {
280 return null;
281 }
282
283 int i = 0;
284 int len = literal.length();
285 char ch;
286 boolean value = false;
287
288 if (literal.length() <= 0) {
289 return null;
290 }
291
292 do {
293 ch = literal.charAt(i++);
294 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len);
295
296 int strIndex = 0;
297
298 switch (ch) {
299 case '1':
300 value = true;
301 break;
302 case '0':
303 value = false;
304 break;
305 case 't':
306 String strTrue = "rue";
307 do {
308 ch = literal.charAt(i++);
309 } while ((strTrue.charAt(strIndex++) == ch) && i < len && strIndex < 3);
310
311 if (strIndex == 3) {
312 value = true;
313 } else {
314 return false;
315 }
316
317
318 break;
319 case 'f':
320 String strFalse = "alse";
321 do {
322 ch = literal.charAt(i++);
323 } while ((strFalse.charAt(strIndex++) == ch) && i < len && strIndex < 4);
324
325
326 if (strIndex == 4) {
327 value = false;
328 } else {
329 return false;
330 }
331
332
333 break;
334 }
335
336 if (i < len) {
337 do {
338 ch = literal.charAt(i++);
339 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len);
340 }
341
342 if (i == len) {
343 return value;
344 } else {
345 return null;
346 }
347
348 }
349
350 public String printBoolean(boolean val) {
351 return val ? "true" : "false";
352 }
353
354 public static String _printBoolean(boolean val) {
355 return val ? "true" : "false";
356 }
357
358 public byte parseByte(String lexicalXSDByte) {
359 return _parseByte(lexicalXSDByte);
360 }
361
362 public static byte _parseByte(CharSequence literal) {
363 return (byte) _parseInt(literal);
364 }
365
366 public String printByte(byte val) {
367 return _printByte(val);
368 }
369
370 public static String _printByte(byte val) {
371 return String.valueOf(val);
372 }
373
374 public QName parseQName(String lexicalXSDQName, NamespaceContext nsc) {
375 return _parseQName(lexicalXSDQName, nsc);
376 }
377
378
381 public static QName _parseQName(CharSequence text, NamespaceContext nsc) {
382 int length = text.length();
383
384
385 int start = 0;
386 while (start < length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start))) {
387 start++;
388 }
389
390 int end = length;
391 while (end > start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end - 1))) {
392 end--;
393 }
394
395 if (end == start) {
396 throw new IllegalArgumentException("input is empty");
397 }
398
399
400 String uri;
401 String localPart;
402 String prefix;
403
404
405 int idx = start + 1;
406 while (idx < end && text.charAt(idx) != ':') {
407 idx++;
408 }
409
410 if (idx == end) {
411 uri = nsc.getNamespaceURI("");
412 localPart = text.subSequence(start, end).toString();
413 prefix = "";
414 } else {
415
416 prefix = text.subSequence(start, idx).toString();
417 localPart = text.subSequence(idx + 1, end).toString();
418 uri = nsc.getNamespaceURI(prefix);
419
420
421 if (uri == null || uri.length() == 0)
422
423 {
424 throw new IllegalArgumentException("prefix " + prefix + " is not bound to a namespace");
425 }
426 }
427
428 return new QName(uri, localPart, prefix);
429 }
430
431 public Calendar parseDateTime(String lexicalXSDDateTime) {
432 return _parseDateTime(lexicalXSDDateTime);
433 }
434
435 public static GregorianCalendar _parseDateTime(CharSequence s) {
436 String val = WhiteSpaceProcessor.trim(s).toString();
437 return datatypeFactory.newXMLGregorianCalendar(val).toGregorianCalendar();
438 }
439
440 public String printDateTime(Calendar val) {
441 return _printDateTime(val);
442 }
443
444 public static String _printDateTime(Calendar val) {
445 return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z", val);
446 }
447
448 public byte[] parseBase64Binary(String lexicalXSDBase64Binary) {
449 return _parseBase64Binary(lexicalXSDBase64Binary);
450 }
451
452 public byte[] parseHexBinary(String s) {
453 final int len = s.length();
454
455
456 if (len % 2 != 0) {
457 throw new IllegalArgumentException("hexBinary needs to be even-length: " + s);
458 }
459
460 byte[] out = new byte[len / 2];
461
462 for (int i = 0; i < len; i += 2) {
463 int h = hexToBin(s.charAt(i));
464 int l = hexToBin(s.charAt(i + 1));
465 if (h == -1 || l == -1) {
466 throw new IllegalArgumentException("contains illegal character for hexBinary: " + s);
467 }
468
469 out[i / 2] = (byte) (h * 16 + l);
470 }
471
472 return out;
473 }
474
475 private static int hexToBin(char ch) {
476 if ('0' <= ch && ch <= '9') {
477 return ch - '0';
478 }
479 if ('A' <= ch && ch <= 'F') {
480 return ch - 'A' + 10;
481 }
482 if ('a' <= ch && ch <= 'f') {
483 return ch - 'a' + 10;
484 }
485 return -1;
486 }
487 private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
488
489 public String printHexBinary(byte[] data) {
490 StringBuilder r = new StringBuilder(data.length * 2);
491 for (byte b : data) {
492 r.append(hexCode[(b >> 4) & 0xF]);
493 r.append(hexCode[(b & 0xF)]);
494 }
495 return r.toString();
496 }
497
498 public long parseUnsignedInt(String lexicalXSDUnsignedInt) {
499 return _parseLong(lexicalXSDUnsignedInt);
500 }
501
502 public String printUnsignedInt(long val) {
503 return _printLong(val);
504 }
505
506 public int parseUnsignedShort(String lexicalXSDUnsignedShort) {
507 return _parseInt(lexicalXSDUnsignedShort);
508 }
509
510 public Calendar parseTime(String lexicalXSDTime) {
511 return datatypeFactory.newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar();
512 }
513
514 public String printTime(Calendar val) {
515 return CalendarFormatter.doFormat("%h:%m:%s%z", val);
516 }
517
518 public Calendar parseDate(String lexicalXSDDate) {
519 return datatypeFactory.newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar();
520 }
521
522 public String printDate(Calendar val) {
523 return _printDate(val);
524 }
525
526 public static String _printDate(Calendar val) {
527 return CalendarFormatter.doFormat((new StringBuilder("%Y-%M-%D").append("%z")).toString(),val);
528 }
529
530 public String parseAnySimpleType(String lexicalXSDAnySimpleType) {
531 return lexicalXSDAnySimpleType;
532
533 }
534
535 public String printString(String val) {
536
537 return val;
538 }
539
540 public String printInt(int val) {
541 return _printInt(val);
542 }
543
544 public static String _printInt(int val) {
545 return String.valueOf(val);
546 }
547
548 public String printLong(long val) {
549 return _printLong(val);
550 }
551
552 public static String _printLong(long val) {
553 return String.valueOf(val);
554 }
555
556 public String printDecimal(BigDecimal val) {
557 return _printDecimal(val);
558 }
559
560 public static String _printDecimal(BigDecimal val) {
561 return val.toPlainString();
562 }
563
564 public String printDouble(double v) {
565 return _printDouble(v);
566 }
567
568 public static String _printDouble(double v) {
569 if (Double.isNaN(v)) {
570 return "NaN";
571 }
572 if (v == Double.POSITIVE_INFINITY) {
573 return "INF";
574 }
575 if (v == Double.NEGATIVE_INFINITY) {
576 return "-INF";
577 }
578 return String.valueOf(v);
579 }
580
581 public String printQName(QName val, NamespaceContext nsc) {
582 return _printQName(val, nsc);
583 }
584
585 public static String _printQName(QName val, NamespaceContext nsc) {
586
587 String qname;
588 String prefix = nsc.getPrefix(val.getNamespaceURI());
589 String localPart = val.getLocalPart();
590
591 if (prefix == null || prefix.length() == 0) {
592 qname = localPart;
593 } else {
594 qname = prefix + ':' + localPart;
595 }
596
597 return qname;
598 }
599
600 public String printBase64Binary(byte[] val) {
601 return _printBase64Binary(val);
602 }
603
604 public String printUnsignedShort(int val) {
605 return String.valueOf(val);
606 }
607
608 public String printAnySimpleType(String val) {
609 return val;
610 }
611
612
617 public static String installHook(String s) {
618 DatatypeConverter.setDatatypeConverter(theInstance);
619 return s;
620 }
621
622 private static final byte[] decodeMap = initDecodeMap();
623 private static final byte PADDING = 127;
624
625 private static byte[] initDecodeMap() {
626 byte[] map = new byte[128];
627 int i;
628 for (i = 0; i < 128; i++) {
629 map[i] = -1;
630 }
631
632 for (i = 'A'; i <= 'Z'; i++) {
633 map[i] = (byte) (i - 'A');
634 }
635 for (i = 'a'; i <= 'z'; i++) {
636 map[i] = (byte) (i - 'a' + 26);
637 }
638 for (i = '0'; i <= '9'; i++) {
639 map[i] = (byte) (i - '0' + 52);
640 }
641 map['+'] = 62;
642 map['/'] = 63;
643 map['='] = PADDING;
644
645 return map;
646 }
647
648
668 private static int guessLength(String text) {
669 final int len = text.length();
670
671
672 int j = len - 1;
673 for (; j >= 0; j--) {
674 byte code = decodeMap[text.charAt(j)];
675 if (code == PADDING) {
676 continue;
677 }
678 if (code == -1)
679 {
680 return text.length() / 4 * 3;
681 }
682 break;
683 }
684
685 j++;
686 int padSize = len - j;
687 if (padSize > 2)
688 {
689 return text.length() / 4 * 3;
690 }
691
692
693
694 return text.length() / 4 * 3 - padSize;
695 }
696
697
706 public static byte[] _parseBase64Binary(String text) {
707 final int buflen = guessLength(text);
708 final byte[] out = new byte[buflen];
709 int o = 0;
710
711 final int len = text.length();
712 int i;
713
714 final byte[] quadruplet = new byte[4];
715 int q = 0;
716
717
718 for (i = 0; i < len; i++) {
719 char ch = text.charAt(i);
720 byte v = decodeMap[ch];
721
722 if (v != -1) {
723 quadruplet[q++] = v;
724 }
725
726 if (q == 4) {
727
728 out[o++] = (byte) ((quadruplet[0] << 2) | (quadruplet[1] >> 4));
729 if (quadruplet[2] != PADDING) {
730 out[o++] = (byte) ((quadruplet[1] << 4) | (quadruplet[2] >> 2));
731 }
732 if (quadruplet[3] != PADDING) {
733 out[o++] = (byte) ((quadruplet[2] << 6) | (quadruplet[3]));
734 }
735 q = 0;
736 }
737 }
738
739 if (buflen == o)
740 {
741 return out;
742 }
743
744
745 byte[] nb = new byte[o];
746 System.arraycopy(out, 0, nb, 0, o);
747 return nb;
748 }
749 private static final char[] encodeMap = initEncodeMap();
750
751 private static char[] initEncodeMap() {
752 char[] map = new char[64];
753 int i;
754 for (i = 0; i < 26; i++) {
755 map[i] = (char) ('A' + i);
756 }
757 for (i = 26; i < 52; i++) {
758 map[i] = (char) ('a' + (i - 26));
759 }
760 for (i = 52; i < 62; i++) {
761 map[i] = (char) ('0' + (i - 52));
762 }
763 map[62] = '+';
764 map[63] = '/';
765
766 return map;
767 }
768
769 public static char encode(int i) {
770 return encodeMap[i & 0x3F];
771 }
772
773 public static byte encodeByte(int i) {
774 return (byte) encodeMap[i & 0x3F];
775 }
776
777 public static String _printBase64Binary(byte[] input) {
778 return _printBase64Binary(input, 0, input.length);
779 }
780
781 public static String _printBase64Binary(byte[] input, int offset, int len) {
782 char[] buf = new char[((len + 2) / 3) * 4];
783 int ptr = _printBase64Binary(input, offset, len, buf, 0);
784 assert ptr == buf.length;
785 return new String(buf);
786 }
787
788
797 public static int _printBase64Binary(byte[] input, int offset, int len, char[] buf, int ptr) {
798
799 int remaining = len;
800 int i;
801 for (i = offset;remaining >= 3; remaining -= 3, i += 3) {
802 buf[ptr++] = encode(input[i] >> 2);
803 buf[ptr++] = encode(
804 ((input[i] & 0x3) << 4)
805 | ((input[i + 1] >> 4) & 0xF));
806 buf[ptr++] = encode(
807 ((input[i + 1] & 0xF) << 2)
808 | ((input[i + 2] >> 6) & 0x3));
809 buf[ptr++] = encode(input[i + 2] & 0x3F);
810 }
811
812 if (remaining == 1) {
813 buf[ptr++] = encode(input[i] >> 2);
814 buf[ptr++] = encode(((input[i]) & 0x3) << 4);
815 buf[ptr++] = '=';
816 buf[ptr++] = '=';
817 }
818
819 if (remaining == 2) {
820 buf[ptr++] = encode(input[i] >> 2);
821 buf[ptr++] = encode(((input[i] & 0x3) << 4)
822 | ((input[i + 1] >> 4) & 0xF));
823 buf[ptr++] = encode((input[i + 1] & 0xF) << 2);
824 buf[ptr++] = '=';
825 }
826 return ptr;
827 }
828
829
839 public static int _printBase64Binary(byte[] input, int offset, int len, byte[] out, int ptr) {
840 byte[] buf = out;
841 int remaining = len;
842 int i;
843 for (i=offset; remaining >= 3; remaining -= 3, i += 3 ) {
844 buf[ptr++] = encodeByte(input[i]>>2);
845 buf[ptr++] = encodeByte(
846 ((input[i]&0x3)<<4) |
847 ((input[i+1]>>4)&0xF));
848 buf[ptr++] = encodeByte(
849 ((input[i+1]&0xF)<<2)|
850 ((input[i+2]>>6)&0x3));
851 buf[ptr++] = encodeByte(input[i+2]&0x3F);
852 }
853
854 if (remaining == 1) {
855 buf[ptr++] = encodeByte(input[i]>>2);
856 buf[ptr++] = encodeByte(((input[i])&0x3)<<4);
857 buf[ptr++] = '=';
858 buf[ptr++] = '=';
859 }
860
861 if (remaining == 2) {
862 buf[ptr++] = encodeByte(input[i]>>2);
863 buf[ptr++] = encodeByte(
864 ((input[i]&0x3)<<4) |
865 ((input[i+1]>>4)&0xF));
866 buf[ptr++] = encodeByte((input[i+1]&0xF)<<2);
867 buf[ptr++] = '=';
868 }
869
870 return ptr;
871 }
872
873 private static CharSequence removeOptionalPlus(CharSequence s) {
874 int len = s.length();
875
876 if (len <= 1 || s.charAt(0) != '+') {
877 return s;
878 }
879
880 s = s.subSequence(1, len);
881 char ch = s.charAt(0);
882 if ('0' <= ch && ch <= '9') {
883 return s;
884 }
885 if ('.' == ch) {
886 return s;
887 }
888
889 throw new NumberFormatException();
890 }
891
892 private static boolean isDigitOrPeriodOrSign(char ch) {
893 if ('0' <= ch && ch <= '9') {
894 return true;
895 }
896 if (ch == '+' || ch == '-' || ch == '.') {
897 return true;
898 }
899 return false;
900 }
901 private static final DatatypeFactory datatypeFactory;
902
903 static {
904 try {
905 datatypeFactory = DatatypeFactory.newInstance();
906 } catch (DatatypeConfigurationException e) {
907 throw new Error(e);
908 }
909 }
910
911 private static final class CalendarFormatter {
912
913 public static String doFormat(String format, Calendar cal) throws IllegalArgumentException {
914 int fidx = 0;
915 int flen = format.length();
916 StringBuilder buf = new StringBuilder();
917
918 while (fidx < flen) {
919 char fch = format.charAt(fidx++);
920
921 if (fch != '%') {
922 buf.append(fch);
923 continue;
924 }
925
926
927 switch (format.charAt(fidx++)) {
928 case 'Y':
929 formatYear(cal, buf);
930 break;
931
932 case 'M':
933 formatMonth(cal, buf);
934 break;
935
936 case 'D':
937 formatDays(cal, buf);
938 break;
939
940 case 'h':
941 formatHours(cal, buf);
942 break;
943
944 case 'm':
945 formatMinutes(cal, buf);
946 break;
947
948 case 's':
949 formatSeconds(cal, buf);
950 break;
951
952 case 'z':
953 formatTimeZone(cal, buf);
954 break;
955
956 default:
957
958 throw new InternalError();
959 }
960 }
961
962 return buf.toString();
963 }
964
965 private static void formatYear(Calendar cal, StringBuilder buf) {
966 int year = cal.get(Calendar.YEAR);
967
968 String s;
969 if (year <= 0)
970 {
971 s = Integer.toString(1 - year);
972 } else
973 {
974 s = Integer.toString(year);
975 }
976
977 while (s.length() < 4) {
978 s = '0' + s;
979 }
980 if (year <= 0) {
981 s = '-' + s;
982 }
983
984 buf.append(s);
985 }
986
987 private static void formatMonth(Calendar cal, StringBuilder buf) {
988 formatTwoDigits(cal.get(Calendar.MONTH) + 1, buf);
989 }
990
991 private static void formatDays(Calendar cal, StringBuilder buf) {
992 formatTwoDigits(cal.get(Calendar.DAY_OF_MONTH), buf);
993 }
994
995 private static void formatHours(Calendar cal, StringBuilder buf) {
996 formatTwoDigits(cal.get(Calendar.HOUR_OF_DAY), buf);
997 }
998
999 private static void formatMinutes(Calendar cal, StringBuilder buf) {
1000 formatTwoDigits(cal.get(Calendar.MINUTE), buf);
1001 }
1002
1003 private static void formatSeconds(Calendar cal, StringBuilder buf) {
1004 formatTwoDigits(cal.get(Calendar.SECOND), buf);
1005 if (cal.isSet(Calendar.MILLISECOND)) {
1006 int n = cal.get(Calendar.MILLISECOND);
1007 if (n != 0) {
1008 String ms = Integer.toString(n);
1009 while (ms.length() < 3) {
1010 ms = '0' + ms;
1011 }
1012 buf.append('.');
1013 buf.append(ms);
1014 }
1015 }
1016 }
1017
1018
1019 private static void formatTimeZone(Calendar cal, StringBuilder buf) {
1020 TimeZone tz = cal.getTimeZone();
1021
1022 if (tz == null) {
1023 return;
1024 }
1025
1026
1027 int offset = tz.getOffset(cal.getTime().getTime());
1028
1029 if (offset == 0) {
1030 buf.append('Z');
1031 return;
1032 }
1033
1034 if (offset >= 0) {
1035 buf.append('+');
1036 } else {
1037 buf.append('-');
1038 offset *= -1;
1039 }
1040
1041 offset /= 60 * 1000;
1042
1043 formatTwoDigits(offset / 60, buf);
1044 buf.append(':');
1045 formatTwoDigits(offset % 60, buf);
1046 }
1047
1048
1049 private static void formatTwoDigits(int n, StringBuilder buf) {
1050
1051 if (n < 10) {
1052 buf.append('0');
1053 }
1054 buf.append(n);
1055 }
1056 }
1057 }
1058