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
26package com.sun.xml.internal.bind;
27
28import java.math.BigDecimal;
29import java.math.BigInteger;
30import java.security.AccessController;
31import java.security.PrivilegedAction;
32import java.util.Calendar;
33import java.util.Collections;
34import java.util.GregorianCalendar;
35import java.util.Map;
36import java.util.TimeZone;
37import java.util.WeakHashMap;
38
39import javax.xml.bind.DatatypeConverter;
40import javax.xml.bind.DatatypeConverterInterface;
41import javax.xml.datatype.DatatypeConfigurationException;
42import javax.xml.datatype.DatatypeFactory;
43import javax.xml.namespace.NamespaceContext;
44import javax.xml.namespace.QName;
45import javax.xml.stream.XMLStreamException;
46import 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
66public 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}
1138