Thu, 31 Aug 2017 15:18:52 +0800
merge
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 */
26 package com.sun.xml.internal.bind;
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;
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;
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 JAXB1.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 {
68 @Deprecated
69 public static final DatatypeConverterInterface theInstance = new DatatypeConverterImpl();
71 protected DatatypeConverterImpl() {
72 // shall not be used
73 }
75 public static BigInteger _parseInteger(CharSequence s) {
76 return new BigInteger(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
77 }
79 public static String _printInteger(BigInteger val) {
80 return val.toString();
81 }
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;
97 int r = 0;
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 }
114 return r * sign;
115 }
117 public static long _parseLong(CharSequence s) {
118 return Long.valueOf(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
119 }
121 public static short _parseShort(CharSequence s) {
122 return (short) _parseInt(s);
123 }
125 public static String _printShort(short val) {
126 return String.valueOf(val);
127 }
129 public static BigDecimal _parseDecimal(CharSequence content) {
130 content = WhiteSpaceProcessor.trim(content);
132 if (content.length() <= 0) {
133 return null;
134 }
136 return new BigDecimal(content.toString());
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 }
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"
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.
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 */
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 }
178 if (s.length() == 0
179 || !isDigitOrPeriodOrSign(s.charAt(0))
180 || !isDigitOrPeriodOrSign(s.charAt(s.length() - 1))) {
181 throw new NumberFormatException();
182 }
184 // these screening process is necessary due to the wobble of Float.valueOf method
185 return Float.parseFloat(s);
186 }
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 }
201 public static double _parseDouble(CharSequence _val) {
202 String val = WhiteSpaceProcessor.trim(_val).toString();
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 }
214 if (val.length() == 0
215 || !isDigitOrPeriodOrSign(val.charAt(0))
216 || !isDigitOrPeriodOrSign(val.charAt(val.length() - 1))) {
217 throw new NumberFormatException(val);
218 }
221 // these screening process is necessary due to the wobble of Float.valueOf method
222 return Double.parseDouble(val);
223 }
225 public static Boolean _parseBoolean(CharSequence literal) {
226 if (literal == null) {
227 return null;
228 }
230 int i = 0;
231 int len = literal.length();
232 char ch;
233 boolean value = false;
235 if (literal.length() <= 0) {
236 return null;
237 }
239 do {
240 ch = literal.charAt(i++);
241 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len);
243 int strIndex = 0;
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);
258 if (strIndex == 3) {
259 value = true;
260 } else {
261 return false;
262 }
263 // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value.");
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);
273 if (strIndex == 4) {
274 value = false;
275 } else {
276 return false;
277 }
278 // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value.");
280 break;
281 }
283 if (i < len) {
284 do {
285 ch = literal.charAt(i++);
286 } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len);
287 }
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 }
297 public static String _printBoolean(boolean val) {
298 return val ? "true" : "false";
299 }
301 public static byte _parseByte(CharSequence literal) {
302 return (byte) _parseInt(literal);
303 }
305 public static String _printByte(byte val) {
306 return String.valueOf(val);
307 }
309 /**
310 * @return null if fails to convert.
311 */
312 public static QName _parseQName(CharSequence text, NamespaceContext nsc) {
313 int length = text.length();
315 // trim whitespace
316 int start = 0;
317 while (start < length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start))) {
318 start++;
319 }
321 int end = length;
322 while (end > start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end - 1))) {
323 end--;
324 }
326 if (end == start) {
327 throw new IllegalArgumentException("input is empty");
328 }
331 String uri;
332 String localPart;
333 String prefix;
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 }
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 }
359 return new QName(uri, localPart, prefix);
360 }
362 public static GregorianCalendar _parseDateTime(CharSequence s) {
363 String val = WhiteSpaceProcessor.trim(s).toString();
364 return getDatatypeFactory().newXMLGregorianCalendar(val).toGregorianCalendar();
365 }
367 public static String _printDateTime(Calendar val) {
368 return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z", val);
369 }
371 public static String _printDate(Calendar val) {
372 return CalendarFormatter.doFormat((new StringBuilder("%Y-%M-%D").append("%z")).toString(),val);
373 }
375 public static String _printInt(int val) {
376 return String.valueOf(val);
377 }
379 public static String _printLong(long val) {
380 return String.valueOf(val);
381 }
383 public static String _printDecimal(BigDecimal val) {
384 return val.toPlainString();
385 }
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 }
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();
406 if (prefix == null || prefix.length() == 0) { // be defensive
407 qname = localPart;
408 } else {
409 qname = prefix + ':' + localPart;
410 }
412 return qname;
413 }
415 // base64 decoder
416 private static final byte[] decodeMap = initDecodeMap();
417 private static final byte PADDING = 127;
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 }
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;
439 return map;
440 }
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();
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 }
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 }
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 }
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;
505 final int len = text.length();
506 int i;
508 final byte[] quadruplet = new byte[4];
509 int q = 0;
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];
516 if (v != -1) {
517 quadruplet[q++] = v;
518 }
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 }
533 if (buflen == o) // speculation worked out to be OK
534 {
535 return out;
536 }
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();
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] = '/';
560 return map;
561 }
563 public static char encode(int i) {
564 return encodeMap[i & 0x3F];
565 }
567 public static byte encodeByte(int i) {
568 return (byte) encodeMap[i & 0x3F];
569 }
571 public static String _printBase64Binary(byte[] input) {
572 return _printBase64Binary(input, 0, input.length);
573 }
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 }
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 }
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];
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 }
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 }
699 return ptr;
700 }
702 private static CharSequence removeOptionalPlus(CharSequence s) {
703 int len = s.length();
705 if (len <= 1 || s.charAt(0) != '+') {
706 return s;
707 }
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 }
718 throw new NumberFormatException();
719 }
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 }
731 private static final Map<ClassLoader, DatatypeFactory> DF_CACHE = Collections.synchronizedMap(new WeakHashMap<ClassLoader, DatatypeFactory>());
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 }
756 private static final class CalendarFormatter {
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();
763 while (fidx < flen) {
764 char fch = format.charAt(fidx++);
766 if (fch != '%') { // not a meta character
767 buf.append(fch);
768 continue;
769 }
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;
777 case 'M': // month
778 formatMonth(cal, buf);
779 break;
781 case 'D': // days
782 formatDays(cal, buf);
783 break;
785 case 'h': // hours
786 formatHours(cal, buf);
787 break;
789 case 'm': // minutes
790 formatMinutes(cal, buf);
791 break;
793 case 's': // parse seconds.
794 formatSeconds(cal, buf);
795 break;
797 case 'z': // time zone
798 formatTimeZone(cal, buf);
799 break;
801 default:
802 // illegal meta character. impossible.
803 throw new InternalError();
804 }
805 }
807 return buf.toString();
808 }
810 private static void formatYear(Calendar cal, StringBuilder buf) {
811 int year = cal.get(Calendar.YEAR);
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 }
822 while (s.length() < 4) {
823 s = '0' + s;
824 }
825 if (year <= 0) {
826 s = '-' + s;
827 }
829 buf.append(s);
830 }
832 private static void formatMonth(Calendar cal, StringBuilder buf) {
833 formatTwoDigits(cal.get(Calendar.MONTH) + 1, buf);
834 }
836 private static void formatDays(Calendar cal, StringBuilder buf) {
837 formatTwoDigits(cal.get(Calendar.DAY_OF_MONTH), buf);
838 }
840 private static void formatHours(Calendar cal, StringBuilder buf) {
841 formatTwoDigits(cal.get(Calendar.HOUR_OF_DAY), buf);
842 }
844 private static void formatMinutes(Calendar cal, StringBuilder buf) {
845 formatTwoDigits(cal.get(Calendar.MINUTE), buf);
846 }
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 }
863 /** formats time zone specifier. */
864 private static void formatTimeZone(Calendar cal, StringBuilder buf) {
865 TimeZone tz = cal.getTimeZone();
867 if (tz == null) {
868 return;
869 }
871 // otherwise print out normally.
872 int offset = tz.getOffset(cal.getTime().getTime());
874 if (offset == 0) {
875 buf.append('Z');
876 return;
877 }
879 if (offset >= 0) {
880 buf.append('+');
881 } else {
882 buf.append('-');
883 offset *= -1;
884 }
886 offset /= 60 * 1000; // offset is in milli-seconds
888 formatTwoDigits(offset / 60, buf);
889 buf.append(':');
890 formatTwoDigits(offset % 60, buf);
891 }
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 }
903 // DEPRECATED METHODS, KEPT FOR JAXB1 GENERATED CLASSES COMPATIBILITY, WILL BE REMOVED IN FUTURE
905 @Deprecated
906 public String parseString(String lexicalXSDString) {
907 return lexicalXSDString;
908 }
910 @Deprecated
911 public BigInteger parseInteger(String lexicalXSDInteger) {
912 return _parseInteger(lexicalXSDInteger);
913 }
915 @Deprecated
916 public String printInteger(BigInteger val) {
917 return _printInteger(val);
918 }
920 @Deprecated
921 public int parseInt(String s) {
922 return _parseInt(s);
923 }
925 @Deprecated
926 public long parseLong(String lexicalXSLong) {
927 return _parseLong(lexicalXSLong);
928 }
930 @Deprecated
931 public short parseShort(String lexicalXSDShort) {
932 return _parseShort(lexicalXSDShort);
933 }
935 @Deprecated
936 public String printShort(short val) {
937 return _printShort(val);
938 }
940 @Deprecated
941 public BigDecimal parseDecimal(String content) {
942 return _parseDecimal(content);
943 }
945 @Deprecated
946 public float parseFloat(String lexicalXSDFloat) {
947 return _parseFloat(lexicalXSDFloat);
948 }
950 @Deprecated
951 public String printFloat(float v) {
952 return _printFloat(v);
953 }
955 @Deprecated
956 public double parseDouble(String lexicalXSDDouble) {
957 return _parseDouble(lexicalXSDDouble);
958 }
960 @Deprecated
961 public boolean parseBoolean(String lexicalXSDBoolean) {
962 Boolean b = _parseBoolean(lexicalXSDBoolean);
963 return (b == null) ? false : b.booleanValue();
964 }
966 @Deprecated
967 public String printBoolean(boolean val) {
968 return val ? "true" : "false";
969 }
971 @Deprecated
972 public byte parseByte(String lexicalXSDByte) {
973 return _parseByte(lexicalXSDByte);
974 }
976 @Deprecated
977 public String printByte(byte val) {
978 return _printByte(val);
979 }
981 @Deprecated
982 public QName parseQName(String lexicalXSDQName, NamespaceContext nsc) {
983 return _parseQName(lexicalXSDQName, nsc);
984 }
986 @Deprecated
987 public Calendar parseDateTime(String lexicalXSDDateTime) {
988 return _parseDateTime(lexicalXSDDateTime);
989 }
991 @Deprecated
992 public String printDateTime(Calendar val) {
993 return _printDateTime(val);
994 }
996 @Deprecated
997 public byte[] parseBase64Binary(String lexicalXSDBase64Binary) {
998 return _parseBase64Binary(lexicalXSDBase64Binary);
999 }
1001 @Deprecated
1002 public byte[] parseHexBinary(String s) {
1003 final int len = s.length();
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 }
1010 byte[] out = new byte[len / 2];
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 }
1019 out[i / 2] = (byte) (h * 16 + l);
1020 }
1022 return out;
1023 }
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 }
1039 @Deprecated
1040 private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
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 }
1052 @Deprecated
1053 public long parseUnsignedInt(String lexicalXSDUnsignedInt) {
1054 return _parseLong(lexicalXSDUnsignedInt);
1055 }
1057 @Deprecated
1058 public String printUnsignedInt(long val) {
1059 return _printLong(val);
1060 }
1062 @Deprecated
1063 public int parseUnsignedShort(String lexicalXSDUnsignedShort) {
1064 return _parseInt(lexicalXSDUnsignedShort);
1065 }
1067 @Deprecated
1068 public Calendar parseTime(String lexicalXSDTime) {
1069 return getDatatypeFactory().newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar();
1070 }
1072 @Deprecated
1073 public String printTime(Calendar val) {
1074 return CalendarFormatter.doFormat("%h:%m:%s%z", val);
1075 }
1077 @Deprecated
1078 public Calendar parseDate(String lexicalXSDDate) {
1079 return getDatatypeFactory().newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar();
1080 }
1082 @Deprecated
1083 public String printDate(Calendar val) {
1084 return _printDate(val);
1085 }
1087 @Deprecated
1088 public String parseAnySimpleType(String lexicalXSDAnySimpleType) {
1089 return lexicalXSDAnySimpleType;
1090 }
1092 @Deprecated
1093 public String printString(String val) {
1094 return val;
1095 }
1097 @Deprecated
1098 public String printInt(int val) {
1099 return _printInt(val);
1100 }
1102 @Deprecated
1103 public String printLong(long val) {
1104 return _printLong(val);
1105 }
1107 @Deprecated
1108 public String printDecimal(BigDecimal val) {
1109 return _printDecimal(val);
1110 }
1112 @Deprecated
1113 public String printDouble(double v) {
1114 return _printDouble(v);
1115 }
1117 @Deprecated
1118 public String printQName(QName val, NamespaceContext nsc) {
1119 return _printQName(val, nsc);
1120 }
1122 @Deprecated
1123 public String printBase64Binary(byte[] val) {
1124 return _printBase64Binary(val);
1125 }
1127 @Deprecated
1128 public String printUnsignedShort(int val) {
1129 return String.valueOf(val);
1130 }
1132 @Deprecated
1133 public String printAnySimpleType(String val) {
1134 return val;
1135 }
1137 }