1 /*
   2  * Copyright (c) 1997, 2014, 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 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.valueOf(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 }