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