src/share/jaxws_classes/javax/xml/bind/DatatypeConverterImpl.java

Fri, 04 Oct 2013 16:21:34 +0100

author
mkos
date
Fri, 04 Oct 2013 16:21:34 +0100
changeset 408
b0610cd08440
parent 397
b99d7e355d4b
child 637
9c07ef4934dd
permissions
-rw-r--r--

8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar

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

mercurial