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 }