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