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