1 /* 2 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.xml.bind; 27 28 import java.math.BigDecimal; 29 import java.math.BigInteger; 30 import java.util.Calendar; 31 import java.util.GregorianCalendar; 32 import java.util.TimeZone; 33 34 import javax.xml.namespace.QName; 35 import javax.xml.namespace.NamespaceContext; 36 import javax.xml.datatype.DatatypeFactory; 37 import javax.xml.datatype.DatatypeConfigurationException; 38 39 /** 40 * This class is the JAXB RI's default implementation of the 41 * {@link DatatypeConverterInterface}. 42 * 43 * <p> 44 * When client applications specify the use of the static print/parse 45 * methods in {@link DatatypeConverter}, it will delegate 46 * to this class. 47 * 48 * <p> 49 * This class is responsible for whitespace normalization. 50 * 51 * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul> 52 * @since JAXB 2.1 53 */ 54 final class DatatypeConverterImpl implements DatatypeConverterInterface { 55 56 /** 57 * To avoid re-creating instances, we cache one instance. 58 */ 59 public static final DatatypeConverterInterface theInstance = new DatatypeConverterImpl(); 60 61 protected DatatypeConverterImpl() { 62 } 63 64 public String parseString(String lexicalXSDString) { 65 return lexicalXSDString; 66 } 67 68 public BigInteger parseInteger(String lexicalXSDInteger) { 69 return _parseInteger(lexicalXSDInteger); 70 } 71 72 public static BigInteger _parseInteger(CharSequence s) { 73 return new BigInteger(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString()); 74 } 75 76 public String printInteger(BigInteger val) { 77 return _printInteger(val); 78 } 79 80 public static String _printInteger(BigInteger val) { 81 return val.toString(); 82 } 83 84 public int parseInt(String s) { 85 return _parseInt(s); 86 } 87 88 /** 89 * Faster but less robust String->int conversion. 90 * 91 * Note that: 92 * <ol> 93 * <li>XML Schema allows '+', but {@link Integer#valueOf(String)} is not. 94 * <li>XML Schema allows leading and trailing (but not in-between) whitespaces. 95 * {@link Integer#valueOf(String)} doesn't allow any. 96 * </ol> 97 */ 98 public static int _parseInt(CharSequence s) { 99 int len = s.length(); 100 int sign = 1; 101 102 int r = 0; 103 104 for (int i = 0; i < len; i++) { 105 char ch = s.charAt(i); 106 if (WhiteSpaceProcessor.isWhiteSpace(ch)) { 107 // skip whitespace 108 } else if ('0' <= ch && ch <= '9') { 109 r = r * 10 + (ch - '0'); 110 } else if (ch == '-') { 111 sign = -1; 112 } else if (ch == '+') { 113 // noop 114 } else { 115 throw new NumberFormatException("Not a number: " + s); 116 } 117 } 118 119 return r * sign; 120 } 121 122 public long parseLong(String lexicalXSLong) { 123 return _parseLong(lexicalXSLong); 124 } 125 126 public static long _parseLong(CharSequence s) { 127 return Long.valueOf(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString()); 128 } 129 130 public short parseShort(String lexicalXSDShort) { 131 return _parseShort(lexicalXSDShort); 132 } 133 134 public static short _parseShort(CharSequence s) { 135 return (short) _parseInt(s); 136 } 137 138 public String printShort(short val) { 139 return _printShort(val); 140 } 141 142 public static String _printShort(short val) { 143 return String.valueOf(val); 144 } 145 146 public BigDecimal parseDecimal(String content) { 147 return _parseDecimal(content); 148 } 149 150 public static BigDecimal _parseDecimal(CharSequence content) { 151 content = WhiteSpaceProcessor.trim(content); 152 153 if (content.length() <= 0) { 154 return null; 155 } 156 157 return new BigDecimal(content.toString()); 158 159 // from purely XML Schema perspective, 160 // this implementation has a problem, since 161 // in xs:decimal "1.0" and "1" is equal whereas the above 162 // code will return different values for those two forms. 163 // 164 // the code was originally using com.sun.msv.datatype.xsd.NumberType.load, 165 // but a profiling showed that the process of normalizing "1.0" into "1" 166 // could take non-trivial time. 167 // 168 // also, from the user's point of view, one might be surprised if 169 // 1 (not 1.0) is returned from "1.000" 170 } 171 172 public float parseFloat(String lexicalXSDFloat) { 173 return _parseFloat(lexicalXSDFloat); 174 } 175 176 public static float _parseFloat(CharSequence _val) { 177 String s = WhiteSpaceProcessor.trim(_val).toString(); 178 /* Incompatibilities of XML Schema's float "xfloat" and Java's float "jfloat" 179 180 * jfloat.valueOf ignores leading and trailing whitespaces, 181 whereas this is not allowed in xfloat. 182 * jfloat.valueOf allows "float type suffix" (f, F) to be 183 appended after float literal (e.g., 1.52e-2f), whereare 184 this is not the case of xfloat. 185 186 gray zone 187 --------- 188 * jfloat allows ".523". And there is no clear statement that mentions 189 this case in xfloat. Although probably this is allowed. 190 * 191 */ 192 193 if (s.equals("NaN")) { 194 return Float.NaN; 195 } 196 if (s.equals("INF")) { 197 return Float.POSITIVE_INFINITY; 198 } 199 if (s.equals("-INF")) { 200 return Float.NEGATIVE_INFINITY; 201 } 202 203 if (s.length() == 0 204 || !isDigitOrPeriodOrSign(s.charAt(0)) 205 || !isDigitOrPeriodOrSign(s.charAt(s.length() - 1))) { 206 throw new NumberFormatException(); 207 } 208 209 // these screening process is necessary due to the wobble of Float.valueOf method 210 return Float.parseFloat(s); 211 } 212 213 public String printFloat(float v) { 214 return _printFloat(v); 215 } 216 217 public static String _printFloat(float v) { 218 if (Float.isNaN(v)) { 219 return "NaN"; 220 } 221 if (v == Float.POSITIVE_INFINITY) { 222 return "INF"; 223 } 224 if (v == Float.NEGATIVE_INFINITY) { 225 return "-INF"; 226 } 227 return String.valueOf(v); 228 } 229 230 public double parseDouble(String lexicalXSDDouble) { 231 return _parseDouble(lexicalXSDDouble); 232 } 233 234 public static double _parseDouble(CharSequence _val) { 235 String val = WhiteSpaceProcessor.trim(_val).toString(); 236 237 if (val.equals("NaN")) { 238 return Double.NaN; 239 } 240 if (val.equals("INF")) { 241 return Double.POSITIVE_INFINITY; 242 } 243 if (val.equals("-INF")) { 244 return Double.NEGATIVE_INFINITY; 245 } 246 247 if (val.length() == 0 248 || !isDigitOrPeriodOrSign(val.charAt(0)) 249 || !isDigitOrPeriodOrSign(val.charAt(val.length() - 1))) { 250 throw new NumberFormatException(val); 251 } 252 253 254 // these screening process is necessary due to the wobble of Float.valueOf method 255 return Double.parseDouble(val); 256 } 257 258 public boolean parseBoolean(String lexicalXSDBoolean) { 259 Boolean b = _parseBoolean(lexicalXSDBoolean); 260 return (b == null) ? false : b.booleanValue(); 261 } 262 263 public static Boolean _parseBoolean(CharSequence literal) { 264 if (literal == null) { 265 return null; 266 } 267 268 int i = 0; 269 int len = literal.length(); 270 char ch; 271 boolean value = false; 272 273 if (literal.length() <= 0) { 274 return null; 275 } 276 277 do { 278 ch = literal.charAt(i++); 279 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len); 280 281 int strIndex = 0; 282 283 switch (ch) { 284 case '1': 285 value = true; 286 break; 287 case '0': 288 value = false; 289 break; 290 case 't': 291 String strTrue = "rue"; 292 do { 293 ch = literal.charAt(i++); 294 } while ((strTrue.charAt(strIndex++) == ch) && i < len && strIndex < 3); 295 296 if (strIndex == 3) { 297 value = true; 298 } else { 299 return false; 300 } 301 // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); 302 303 break; 304 case 'f': 305 String strFalse = "alse"; 306 do { 307 ch = literal.charAt(i++); 308 } while ((strFalse.charAt(strIndex++) == ch) && i < len && strIndex < 4); 309 310 311 if (strIndex == 4) { 312 value = false; 313 } else { 314 return false; 315 } 316 // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); 317 318 break; 319 } 320 321 if (i < len) { 322 do { 323 ch = literal.charAt(i++); 324 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len); 325 } 326 327 if (i == len) { 328 return value; 329 } else { 330 return null; 331 } 332 // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); 333 } 334 335 public String printBoolean(boolean val) { 336 return val ? "true" : "false"; 337 } 338 339 public static String _printBoolean(boolean val) { 340 return val ? "true" : "false"; 341 } 342 343 public byte parseByte(String lexicalXSDByte) { 344 return _parseByte(lexicalXSDByte); 345 } 346 347 public static byte _parseByte(CharSequence literal) { 348 return (byte) _parseInt(literal); 349 } 350 351 public String printByte(byte val) { 352 return _printByte(val); 353 } 354 355 public static String _printByte(byte val) { 356 return String.valueOf(val); 357 } 358 359 public QName parseQName(String lexicalXSDQName, NamespaceContext nsc) { 360 return _parseQName(lexicalXSDQName, nsc); 361 } 362 363 /** 364 * @return null if fails to convert. 365 */ 366 public static QName _parseQName(CharSequence text, NamespaceContext nsc) { 367 int length = text.length(); 368 369 // trim whitespace 370 int start = 0; 371 while (start < length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start))) { 372 start++; 373 } 374 375 int end = length; 376 while (end > start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end - 1))) { 377 end--; 378 } 379 380 if (end == start) { 381 throw new IllegalArgumentException("input is empty"); 382 } 383 384 385 String uri; 386 String localPart; 387 String prefix; 388 389 // search ':' 390 int idx = start + 1; // no point in searching the first char. that's not valid. 391 while (idx < end && text.charAt(idx) != ':') { 392 idx++; 393 } 394 395 if (idx == end) { 396 uri = nsc.getNamespaceURI(""); 397 localPart = text.subSequence(start, end).toString(); 398 prefix = ""; 399 } else { 400 // Prefix exists, check everything 401 prefix = text.subSequence(start, idx).toString(); 402 localPart = text.subSequence(idx + 1, end).toString(); 403 uri = nsc.getNamespaceURI(prefix); 404 // uri can never be null according to javadoc, 405 // but some users reported that there are implementations that return null. 406 if (uri == null || uri.length() == 0) // crap. the NamespaceContext interface is broken. 407 // error: unbound prefix 408 { 409 throw new IllegalArgumentException("prefix " + prefix + " is not bound to a namespace"); 410 } 411 } 412 413 return new QName(uri, localPart, prefix); 414 } 415 416 public Calendar parseDateTime(String lexicalXSDDateTime) { 417 return _parseDateTime(lexicalXSDDateTime); 418 } 419 420 public static GregorianCalendar _parseDateTime(CharSequence s) { 421 String val = WhiteSpaceProcessor.trim(s).toString(); 422 return datatypeFactory.newXMLGregorianCalendar(val).toGregorianCalendar(); 423 } 424 425 public String printDateTime(Calendar val) { 426 return _printDateTime(val); 427 } 428 429 public static String _printDateTime(Calendar val) { 430 return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z", val); 431 } 432 433 public byte[] parseBase64Binary(String lexicalXSDBase64Binary) { 434 return _parseBase64Binary(lexicalXSDBase64Binary); 435 } 436 437 public byte[] parseHexBinary(String s) { 438 final int len = s.length(); 439 440 // "111" is not a valid hex encoding. 441 if (len % 2 != 0) { 442 throw new IllegalArgumentException("hexBinary needs to be even-length: " + s); 443 } 444 445 byte[] out = new byte[len / 2]; 446 447 for (int i = 0; i < len; i += 2) { 448 int h = hexToBin(s.charAt(i)); 449 int l = hexToBin(s.charAt(i + 1)); 450 if (h == -1 || l == -1) { 451 throw new IllegalArgumentException("contains illegal character for hexBinary: " + s); 452 } 453 454 out[i / 2] = (byte) (h * 16 + l); 455 } 456 457 return out; 458 } 459 460 private static int hexToBin(char ch) { 461 if ('0' <= ch && ch <= '9') { 462 return ch - '0'; 463 } 464 if ('A' <= ch && ch <= 'F') { 465 return ch - 'A' + 10; 466 } 467 if ('a' <= ch && ch <= 'f') { 468 return ch - 'a' + 10; 469 } 470 return -1; 471 } 472 private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); 473 474 public String printHexBinary(byte[] data) { 475 StringBuilder r = new StringBuilder(data.length * 2); 476 for (byte b : data) { 477 r.append(hexCode[(b >> 4) & 0xF]); 478 r.append(hexCode[(b & 0xF)]); 479 } 480 return r.toString(); 481 } 482 483 public long parseUnsignedInt(String lexicalXSDUnsignedInt) { 484 return _parseLong(lexicalXSDUnsignedInt); 485 } 486 487 public String printUnsignedInt(long val) { 488 return _printLong(val); 489 } 490 491 public int parseUnsignedShort(String lexicalXSDUnsignedShort) { 492 return _parseInt(lexicalXSDUnsignedShort); 493 } 494 495 public Calendar parseTime(String lexicalXSDTime) { 496 return datatypeFactory.newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar(); 497 } 498 499 public String printTime(Calendar val) { 500 return CalendarFormatter.doFormat("%h:%m:%s%z", val); 501 } 502 503 public Calendar parseDate(String lexicalXSDDate) { 504 return datatypeFactory.newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar(); 505 } 506 507 public String printDate(Calendar val) { 508 return _printDate(val); 509 } 510 511 public static String _printDate(Calendar val) { 512 return CalendarFormatter.doFormat((new StringBuilder("%Y-%M-%D").append("%z")).toString(),val); 513 } 514 515 public String parseAnySimpleType(String lexicalXSDAnySimpleType) { 516 return lexicalXSDAnySimpleType; 517 // return (String)SimpleURType.theInstance._createValue( lexicalXSDAnySimpleType, null ); 518 } 519 520 public String printString(String val) { 521 // return StringType.theInstance.convertToLexicalValue( val, null ); 522 return val; 523 } 524 525 public String printInt(int val) { 526 return _printInt(val); 527 } 528 529 public static String _printInt(int val) { 530 return String.valueOf(val); 531 } 532 533 public String printLong(long val) { 534 return _printLong(val); 535 } 536 537 public static String _printLong(long val) { 538 return String.valueOf(val); 539 } 540 541 public String printDecimal(BigDecimal val) { 542 return _printDecimal(val); 543 } 544 545 public static String _printDecimal(BigDecimal val) { 546 return val.toPlainString(); 547 } 548 549 public String printDouble(double v) { 550 return _printDouble(v); 551 } 552 553 public static String _printDouble(double v) { 554 if (Double.isNaN(v)) { 555 return "NaN"; 556 } 557 if (v == Double.POSITIVE_INFINITY) { 558 return "INF"; 559 } 560 if (v == Double.NEGATIVE_INFINITY) { 561 return "-INF"; 562 } 563 return String.valueOf(v); 564 } 565 566 public String printQName(QName val, NamespaceContext nsc) { 567 return _printQName(val, nsc); 568 } 569 570 public static String _printQName(QName val, NamespaceContext nsc) { 571 // Double-check 572 String qname; 573 String prefix = nsc.getPrefix(val.getNamespaceURI()); 574 String localPart = val.getLocalPart(); 575 576 if (prefix == null || prefix.length() == 0) { // be defensive 577 qname = localPart; 578 } else { 579 qname = prefix + ':' + localPart; 580 } 581 582 return qname; 583 } 584 585 public String printBase64Binary(byte[] val) { 586 return _printBase64Binary(val); 587 } 588 589 public String printUnsignedShort(int val) { 590 return String.valueOf(val); 591 } 592 593 public String printAnySimpleType(String val) { 594 return val; 595 } 596 597 /** 598 * Just return the string passed as a parameter but 599 * installs an instance of this class as the DatatypeConverter 600 * implementation. Used from static fixed value initializers. 601 */ 602 public static String installHook(String s) { 603 DatatypeConverter.setDatatypeConverter(theInstance); 604 return s; 605 } 606 // base64 decoder 607 private static final byte[] decodeMap = initDecodeMap(); 608 private static final byte PADDING = 127; 609 610 private static byte[] initDecodeMap() { 611 byte[] map = new byte[128]; 612 int i; 613 for (i = 0; i < 128; i++) { 614 map[i] = -1; 615 } 616 617 for (i = 'A'; i <= 'Z'; i++) { 618 map[i] = (byte) (i - 'A'); 619 } 620 for (i = 'a'; i <= 'z'; i++) { 621 map[i] = (byte) (i - 'a' + 26); 622 } 623 for (i = '0'; i <= '9'; i++) { 624 map[i] = (byte) (i - '0' + 52); 625 } 626 map['+'] = 62; 627 map['/'] = 63; 628 map['='] = PADDING; 629 630 return map; 631 } 632 633 /** 634 * computes the length of binary data speculatively. 635 * 636 * <p> 637 * Our requirement is to create byte[] of the exact length to store the binary data. 638 * If we do this in a straight-forward way, it takes two passes over the data. 639 * Experiments show that this is a non-trivial overhead (35% or so is spent on 640 * the first pass in calculating the length.) 641 * 642 * <p> 643 * So the approach here is that we compute the length speculatively, without looking 644 * at the whole contents. The obtained speculative value is never less than the 645 * actual length of the binary data, but it may be bigger. So if the speculation 646 * goes wrong, we'll pay the cost of reallocation and buffer copying. 647 * 648 * <p> 649 * If the base64 text is tightly packed with no indentation nor illegal char 650 * (like what most web services produce), then the speculation of this method 651 * will be correct, so we get the performance benefit. 652 */ 653 private static int guessLength(String text) { 654 final int len = text.length(); 655 656 // compute the tail '=' chars 657 int j = len - 1; 658 for (; j >= 0; j--) { 659 byte code = decodeMap[text.charAt(j)]; 660 if (code == PADDING) { 661 continue; 662 } 663 if (code == -1) // most likely this base64 text is indented. go with the upper bound 664 { 665 return text.length() / 4 * 3; 666 } 667 break; 668 } 669 670 j++; // text.charAt(j) is now at some base64 char, so +1 to make it the size 671 int padSize = len - j; 672 if (padSize > 2) // something is wrong with base64. be safe and go with the upper bound 673 { 674 return text.length() / 4 * 3; 675 } 676 677 // so far this base64 looks like it's unindented tightly packed base64. 678 // take a chance and create an array with the expected size 679 return text.length() / 4 * 3 - padSize; 680 } 681 682 /** 683 * @param text 684 * base64Binary data is likely to be long, and decoding requires 685 * each character to be accessed twice (once for counting length, another 686 * for decoding.) 687 * 688 * A benchmark showed that taking {@link String} is faster, presumably 689 * because JIT can inline a lot of string access (with data of 1K chars, it was twice as fast) 690 */ 691 public static byte[] _parseBase64Binary(String text) { 692 final int buflen = guessLength(text); 693 final byte[] out = new byte[buflen]; 694 int o = 0; 695 696 final int len = text.length(); 697 int i; 698 699 final byte[] quadruplet = new byte[4]; 700 int q = 0; 701 702 // convert each quadruplet to three bytes. 703 for (i = 0; i < len; i++) { 704 char ch = text.charAt(i); 705 byte v = decodeMap[ch]; 706 707 if (v != -1) { 708 quadruplet[q++] = v; 709 } 710 711 if (q == 4) { 712 // quadruplet is now filled. 713 out[o++] = (byte) ((quadruplet[0] << 2) | (quadruplet[1] >> 4)); 714 if (quadruplet[2] != PADDING) { 715 out[o++] = (byte) ((quadruplet[1] << 4) | (quadruplet[2] >> 2)); 716 } 717 if (quadruplet[3] != PADDING) { 718 out[o++] = (byte) ((quadruplet[2] << 6) | (quadruplet[3])); 719 } 720 q = 0; 721 } 722 } 723 724 if (buflen == o) // speculation worked out to be OK 725 { 726 return out; 727 } 728 729 // we overestimated, so need to create a new buffer 730 byte[] nb = new byte[o]; 731 System.arraycopy(out, 0, nb, 0, o); 732 return nb; 733 } 734 private static final char[] encodeMap = initEncodeMap(); 735 736 private static char[] initEncodeMap() { 737 char[] map = new char[64]; 738 int i; 739 for (i = 0; i < 26; i++) { 740 map[i] = (char) ('A' + i); 741 } 742 for (i = 26; i < 52; i++) { 743 map[i] = (char) ('a' + (i - 26)); 744 } 745 for (i = 52; i < 62; i++) { 746 map[i] = (char) ('0' + (i - 52)); 747 } 748 map[62] = '+'; 749 map[63] = '/'; 750 751 return map; 752 } 753 754 public static char encode(int i) { 755 return encodeMap[i & 0x3F]; 756 } 757 758 public static byte encodeByte(int i) { 759 return (byte) encodeMap[i & 0x3F]; 760 } 761 762 public static String _printBase64Binary(byte[] input) { 763 return _printBase64Binary(input, 0, input.length); 764 } 765 766 public static String _printBase64Binary(byte[] input, int offset, int len) { 767 char[] buf = new char[((len + 2) / 3) * 4]; 768 int ptr = _printBase64Binary(input, offset, len, buf, 0); 769 assert ptr == buf.length; 770 return new String(buf); 771 } 772 773 /** 774 * Encodes a byte array into a char array by doing base64 encoding. 775 * 776 * The caller must supply a big enough buffer. 777 * 778 * @return 779 * the value of {@code ptr+((len+2)/3)*4}, which is the new offset 780 * in the output buffer where the further bytes should be placed. 781 */ 782 public static int _printBase64Binary(byte[] input, int offset, int len, char[] buf, int ptr) { 783 // encode elements until only 1 or 2 elements are left to encode 784 int remaining = len; 785 int i; 786 for (i = offset;remaining >= 3; remaining -= 3, i += 3) { 787 buf[ptr++] = encode(input[i] >> 2); 788 buf[ptr++] = encode( 789 ((input[i] & 0x3) << 4) 790 | ((input[i + 1] >> 4) & 0xF)); 791 buf[ptr++] = encode( 792 ((input[i + 1] & 0xF) << 2) 793 | ((input[i + 2] >> 6) & 0x3)); 794 buf[ptr++] = encode(input[i + 2] & 0x3F); 795 } 796 // encode when exactly 1 element (left) to encode 797 if (remaining == 1) { 798 buf[ptr++] = encode(input[i] >> 2); 799 buf[ptr++] = encode(((input[i]) & 0x3) << 4); 800 buf[ptr++] = '='; 801 buf[ptr++] = '='; 802 } 803 // encode when exactly 2 elements (left) to encode 804 if (remaining == 2) { 805 buf[ptr++] = encode(input[i] >> 2); 806 buf[ptr++] = encode(((input[i] & 0x3) << 4) 807 | ((input[i + 1] >> 4) & 0xF)); 808 buf[ptr++] = encode((input[i + 1] & 0xF) << 2); 809 buf[ptr++] = '='; 810 } 811 return ptr; 812 } 813 814 /** 815 * Encodes a byte array into another byte array by first doing base64 encoding 816 * then encoding the result in ASCII. 817 * 818 * The caller must supply a big enough buffer. 819 * 820 * @return 821 * the value of {@code ptr+((len+2)/3)*4}, which is the new offset 822 * in the output buffer where the further bytes should be placed. 823 */ 824 public static int _printBase64Binary(byte[] input, int offset, int len, byte[] out, int ptr) { 825 byte[] buf = out; 826 int remaining = len; 827 int i; 828 for (i=offset; remaining >= 3; remaining -= 3, i += 3 ) { 829 buf[ptr++] = encodeByte(input[i]>>2); 830 buf[ptr++] = encodeByte( 831 ((input[i]&0x3)<<4) | 832 ((input[i+1]>>4)&0xF)); 833 buf[ptr++] = encodeByte( 834 ((input[i+1]&0xF)<<2)| 835 ((input[i+2]>>6)&0x3)); 836 buf[ptr++] = encodeByte(input[i+2]&0x3F); 837 } 838 // encode when exactly 1 element (left) to encode 839 if (remaining == 1) { 840 buf[ptr++] = encodeByte(input[i]>>2); 841 buf[ptr++] = encodeByte(((input[i])&0x3)<<4); 842 buf[ptr++] = '='; 843 buf[ptr++] = '='; 844 } 845 // encode when exactly 2 elements (left) to encode 846 if (remaining == 2) { 847 buf[ptr++] = encodeByte(input[i]>>2); 848 buf[ptr++] = encodeByte( 849 ((input[i]&0x3)<<4) | 850 ((input[i+1]>>4)&0xF)); 851 buf[ptr++] = encodeByte((input[i+1]&0xF)<<2); 852 buf[ptr++] = '='; 853 } 854 855 return ptr; 856 } 857 858 private static CharSequence removeOptionalPlus(CharSequence s) { 859 int len = s.length(); 860 861 if (len <= 1 || s.charAt(0) != '+') { 862 return s; 863 } 864 865 s = s.subSequence(1, len); 866 char ch = s.charAt(0); 867 if ('0' <= ch && ch <= '9') { 868 return s; 869 } 870 if ('.' == ch) { 871 return s; 872 } 873 874 throw new NumberFormatException(); 875 } 876 877 private static boolean isDigitOrPeriodOrSign(char ch) { 878 if ('0' <= ch && ch <= '9') { 879 return true; 880 } 881 if (ch == '+' || ch == '-' || ch == '.') { 882 return true; 883 } 884 return false; 885 } 886 private static final DatatypeFactory datatypeFactory; 887 888 static { 889 try { 890 datatypeFactory = DatatypeFactory.newInstance(); 891 } catch (DatatypeConfigurationException e) { 892 throw new Error(e); 893 } 894 } 895 896 private static final class CalendarFormatter { 897 898 public static String doFormat(String format, Calendar cal) throws IllegalArgumentException { 899 int fidx = 0; 900 int flen = format.length(); 901 StringBuilder buf = new StringBuilder(); 902 903 while (fidx < flen) { 904 char fch = format.charAt(fidx++); 905 906 if (fch != '%') { // not a meta character 907 buf.append(fch); 908 continue; 909 } 910 911 // seen meta character. we don't do error check against the format 912 switch (format.charAt(fidx++)) { 913 case 'Y': // year 914 formatYear(cal, buf); 915 break; 916 917 case 'M': // month 918 formatMonth(cal, buf); 919 break; 920 921 case 'D': // days 922 formatDays(cal, buf); 923 break; 924 925 case 'h': // hours 926 formatHours(cal, buf); 927 break; 928 929 case 'm': // minutes 930 formatMinutes(cal, buf); 931 break; 932 933 case 's': // parse seconds. 934 formatSeconds(cal, buf); 935 break; 936 937 case 'z': // time zone 938 formatTimeZone(cal, buf); 939 break; 940 941 default: 942 // illegal meta character. impossible. 943 throw new InternalError(); 944 } 945 } 946 947 return buf.toString(); 948 } 949 950 private static void formatYear(Calendar cal, StringBuilder buf) { 951 int year = cal.get(Calendar.YEAR); 952 953 String s; 954 if (year <= 0) // negative value 955 { 956 s = Integer.toString(1 - year); 957 } else // positive value 958 { 959 s = Integer.toString(year); 960 } 961 962 while (s.length() < 4) { 963 s = '0' + s; 964 } 965 if (year <= 0) { 966 s = '-' + s; 967 } 968 969 buf.append(s); 970 } 971 972 private static void formatMonth(Calendar cal, StringBuilder buf) { 973 formatTwoDigits(cal.get(Calendar.MONTH) + 1, buf); 974 } 975 976 private static void formatDays(Calendar cal, StringBuilder buf) { 977 formatTwoDigits(cal.get(Calendar.DAY_OF_MONTH), buf); 978 } 979 980 private static void formatHours(Calendar cal, StringBuilder buf) { 981 formatTwoDigits(cal.get(Calendar.HOUR_OF_DAY), buf); 982 } 983 984 private static void formatMinutes(Calendar cal, StringBuilder buf) { 985 formatTwoDigits(cal.get(Calendar.MINUTE), buf); 986 } 987 988 private static void formatSeconds(Calendar cal, StringBuilder buf) { 989 formatTwoDigits(cal.get(Calendar.SECOND), buf); 990 if (cal.isSet(Calendar.MILLISECOND)) { // milliseconds 991 int n = cal.get(Calendar.MILLISECOND); 992 if (n != 0) { 993 String ms = Integer.toString(n); 994 while (ms.length() < 3) { 995 ms = '0' + ms; // left 0 paddings. 996 } 997 buf.append('.'); 998 buf.append(ms); 999 } 1000 } 1001 } 1002 1003 /** formats time zone specifier. */ 1004 private static void formatTimeZone(Calendar cal, StringBuilder buf) { 1005 TimeZone tz = cal.getTimeZone(); 1006 1007 if (tz == null) { 1008 return; 1009 } 1010 1011 // otherwise print out normally. 1012 int offset = tz.getOffset(cal.getTime().getTime()); 1013 1014 if (offset == 0) { 1015 buf.append('Z'); 1016 return; 1017 } 1018 1019 if (offset >= 0) { 1020 buf.append('+'); 1021 } else { 1022 buf.append('-'); 1023 offset *= -1; 1024 } 1025 1026 offset /= 60 * 1000; // offset is in milli-seconds 1027 1028 formatTwoDigits(offset / 60, buf); 1029 buf.append(':'); 1030 formatTwoDigits(offset % 60, buf); 1031 } 1032 1033 /** formats Integer into two-character-wide string. */ 1034 private static void formatTwoDigits(int n, StringBuilder buf) { 1035 // n is always non-negative. 1036 if (n < 10) { 1037 buf.append('0'); 1038 } 1039 buf.append(n); 1040 } 1041 } 1042 }