aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind; aoqi@0: aoqi@0: import java.math.BigDecimal; aoqi@0: import java.math.BigInteger; aoqi@0: import java.security.AccessController; aoqi@0: import java.security.PrivilegedAction; aoqi@0: import java.util.Calendar; aoqi@0: import java.util.Collections; aoqi@0: import java.util.GregorianCalendar; aoqi@0: import java.util.Map; aoqi@0: import java.util.TimeZone; aoqi@0: import java.util.WeakHashMap; aoqi@0: aoqi@0: import javax.xml.bind.DatatypeConverter; aoqi@0: import javax.xml.bind.DatatypeConverterInterface; aoqi@0: import javax.xml.datatype.DatatypeConfigurationException; aoqi@0: import javax.xml.datatype.DatatypeFactory; aoqi@0: import javax.xml.namespace.NamespaceContext; aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.XMLStreamWriter; aoqi@0: aoqi@0: /** aoqi@0: * This class is the JAXB RI's default implementation of the aoqi@0: * {@link DatatypeConverterInterface}. aoqi@0: * aoqi@0: *

aoqi@0: * When client applications specify the use of the static print/parse aoqi@0: * methods in {@link DatatypeConverter}, it will delegate aoqi@0: * to this class. aoqi@0: * aoqi@0: *

aoqi@0: * This class is responsible for whitespace normalization. aoqi@0: * aoqi@0: * @author

aoqi@0: * @since JAXB1.0 aoqi@0: * @deprecated in JAXB 2.2.4 - use javax.xml.bind.DatatypeConverterImpl instead aoqi@0: * or let us know why you can't aoqi@0: */ aoqi@0: @Deprecated aoqi@0: public final class DatatypeConverterImpl implements DatatypeConverterInterface { aoqi@0: aoqi@0: @Deprecated aoqi@0: public static final DatatypeConverterInterface theInstance = new DatatypeConverterImpl(); aoqi@0: aoqi@0: protected DatatypeConverterImpl() { aoqi@0: // shall not be used aoqi@0: } aoqi@0: aoqi@0: public static BigInteger _parseInteger(CharSequence s) { aoqi@0: return new BigInteger(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString()); aoqi@0: } aoqi@0: aoqi@0: public static String _printInteger(BigInteger val) { aoqi@0: return val.toString(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Faster but less robust String->int conversion. aoqi@0: * aoqi@0: * Note that: aoqi@0: *
    aoqi@0: *
  1. XML Schema allows '+', but {@link Integer#valueOf(String)} is not. aoqi@0: *
  2. XML Schema allows leading and trailing (but not in-between) whitespaces. aoqi@0: * {@link Integer#valueOf(String)} doesn't allow any. aoqi@0: *
aoqi@0: */ aoqi@0: public static int _parseInt(CharSequence s) { aoqi@0: int len = s.length(); aoqi@0: int sign = 1; aoqi@0: aoqi@0: int r = 0; aoqi@0: aoqi@0: for (int i = 0; i < len; i++) { aoqi@0: char ch = s.charAt(i); aoqi@0: if (WhiteSpaceProcessor.isWhiteSpace(ch)) { aoqi@0: // skip whitespace aoqi@0: } else if ('0' <= ch && ch <= '9') { aoqi@0: r = r * 10 + (ch - '0'); aoqi@0: } else if (ch == '-') { aoqi@0: sign = -1; aoqi@0: } else if (ch == '+') { aoqi@0: // noop aoqi@0: } else { aoqi@0: throw new NumberFormatException("Not a number: " + s); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return r * sign; aoqi@0: } aoqi@0: aoqi@0: public static long _parseLong(CharSequence s) { aoqi@0: return Long.valueOf(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString()); aoqi@0: } aoqi@0: aoqi@0: public static short _parseShort(CharSequence s) { aoqi@0: return (short) _parseInt(s); aoqi@0: } aoqi@0: aoqi@0: public static String _printShort(short val) { aoqi@0: return String.valueOf(val); aoqi@0: } aoqi@0: aoqi@0: public static BigDecimal _parseDecimal(CharSequence content) { aoqi@0: content = WhiteSpaceProcessor.trim(content); aoqi@0: aoqi@0: if (content.length() <= 0) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: return new BigDecimal(content.toString()); aoqi@0: aoqi@0: // from purely XML Schema perspective, aoqi@0: // this implementation has a problem, since aoqi@0: // in xs:decimal "1.0" and "1" is equal whereas the above aoqi@0: // code will return different values for those two forms. aoqi@0: // aoqi@0: // the code was originally using com.sun.msv.datatype.xsd.NumberType.load, aoqi@0: // but a profiling showed that the process of normalizing "1.0" into "1" aoqi@0: // could take non-trivial time. aoqi@0: // aoqi@0: // also, from the user's point of view, one might be surprised if aoqi@0: // 1 (not 1.0) is returned from "1.000" aoqi@0: } aoqi@0: aoqi@0: public static float _parseFloat(CharSequence _val) { aoqi@0: String s = WhiteSpaceProcessor.trim(_val).toString(); aoqi@0: /* Incompatibilities of XML Schema's float "xfloat" and Java's float "jfloat" aoqi@0: aoqi@0: * jfloat.valueOf ignores leading and trailing whitespaces, aoqi@0: whereas this is not allowed in xfloat. aoqi@0: * jfloat.valueOf allows "float type suffix" (f, F) to be aoqi@0: appended after float literal (e.g., 1.52e-2f), whereare aoqi@0: this is not the case of xfloat. aoqi@0: aoqi@0: gray zone aoqi@0: --------- aoqi@0: * jfloat allows ".523". And there is no clear statement that mentions aoqi@0: this case in xfloat. Although probably this is allowed. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: if (s.equals("NaN")) { aoqi@0: return Float.NaN; aoqi@0: } aoqi@0: if (s.equals("INF")) { aoqi@0: return Float.POSITIVE_INFINITY; aoqi@0: } aoqi@0: if (s.equals("-INF")) { aoqi@0: return Float.NEGATIVE_INFINITY; aoqi@0: } aoqi@0: aoqi@0: if (s.length() == 0 aoqi@0: || !isDigitOrPeriodOrSign(s.charAt(0)) aoqi@0: || !isDigitOrPeriodOrSign(s.charAt(s.length() - 1))) { aoqi@0: throw new NumberFormatException(); aoqi@0: } aoqi@0: aoqi@0: // these screening process is necessary due to the wobble of Float.valueOf method aoqi@0: return Float.parseFloat(s); aoqi@0: } aoqi@0: aoqi@0: public static String _printFloat(float v) { aoqi@0: if (Float.isNaN(v)) { aoqi@0: return "NaN"; aoqi@0: } aoqi@0: if (v == Float.POSITIVE_INFINITY) { aoqi@0: return "INF"; aoqi@0: } aoqi@0: if (v == Float.NEGATIVE_INFINITY) { aoqi@0: return "-INF"; aoqi@0: } aoqi@0: return String.valueOf(v); aoqi@0: } aoqi@0: aoqi@0: public static double _parseDouble(CharSequence _val) { aoqi@0: String val = WhiteSpaceProcessor.trim(_val).toString(); aoqi@0: aoqi@0: if (val.equals("NaN")) { aoqi@0: return Double.NaN; aoqi@0: } aoqi@0: if (val.equals("INF")) { aoqi@0: return Double.POSITIVE_INFINITY; aoqi@0: } aoqi@0: if (val.equals("-INF")) { aoqi@0: return Double.NEGATIVE_INFINITY; aoqi@0: } aoqi@0: aoqi@0: if (val.length() == 0 aoqi@0: || !isDigitOrPeriodOrSign(val.charAt(0)) aoqi@0: || !isDigitOrPeriodOrSign(val.charAt(val.length() - 1))) { aoqi@0: throw new NumberFormatException(val); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // these screening process is necessary due to the wobble of Float.valueOf method aoqi@0: return Double.parseDouble(val); aoqi@0: } aoqi@0: aoqi@0: public static Boolean _parseBoolean(CharSequence literal) { aoqi@0: if (literal == null) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: int i = 0; aoqi@0: int len = literal.length(); aoqi@0: char ch; aoqi@0: boolean value = false; aoqi@0: aoqi@0: if (literal.length() <= 0) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: do { aoqi@0: ch = literal.charAt(i++); aoqi@0: } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len); aoqi@0: aoqi@0: int strIndex = 0; aoqi@0: aoqi@0: switch (ch) { aoqi@0: case '1': aoqi@0: value = true; aoqi@0: break; aoqi@0: case '0': aoqi@0: value = false; aoqi@0: break; aoqi@0: case 't': aoqi@0: String strTrue = "rue"; aoqi@0: do { aoqi@0: ch = literal.charAt(i++); aoqi@0: } while ((strTrue.charAt(strIndex++) == ch) && i < len && strIndex < 3); aoqi@0: aoqi@0: if (strIndex == 3) { aoqi@0: value = true; aoqi@0: } else { aoqi@0: return false; aoqi@0: } aoqi@0: // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); aoqi@0: aoqi@0: break; aoqi@0: case 'f': aoqi@0: String strFalse = "alse"; aoqi@0: do { aoqi@0: ch = literal.charAt(i++); aoqi@0: } while ((strFalse.charAt(strIndex++) == ch) && i < len && strIndex < 4); aoqi@0: aoqi@0: aoqi@0: if (strIndex == 4) { aoqi@0: value = false; aoqi@0: } else { aoqi@0: return false; aoqi@0: } aoqi@0: // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); aoqi@0: aoqi@0: break; aoqi@0: } aoqi@0: aoqi@0: if (i < len) { aoqi@0: do { aoqi@0: ch = literal.charAt(i++); aoqi@0: } while (WhiteSpaceProcessor.isWhiteSpace(ch) && i < len); aoqi@0: } aoqi@0: aoqi@0: if (i == len) { aoqi@0: return value; aoqi@0: } else { aoqi@0: return null; aoqi@0: } aoqi@0: // throw new IllegalArgumentException("String \"" + literal + "\" is not valid boolean value."); aoqi@0: } aoqi@0: aoqi@0: public static String _printBoolean(boolean val) { aoqi@0: return val ? "true" : "false"; aoqi@0: } aoqi@0: aoqi@0: public static byte _parseByte(CharSequence literal) { aoqi@0: return (byte) _parseInt(literal); aoqi@0: } aoqi@0: aoqi@0: public static String _printByte(byte val) { aoqi@0: return String.valueOf(val); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @return null if fails to convert. aoqi@0: */ aoqi@0: public static QName _parseQName(CharSequence text, NamespaceContext nsc) { aoqi@0: int length = text.length(); aoqi@0: aoqi@0: // trim whitespace aoqi@0: int start = 0; aoqi@0: while (start < length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start))) { aoqi@0: start++; aoqi@0: } aoqi@0: aoqi@0: int end = length; aoqi@0: while (end > start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end - 1))) { aoqi@0: end--; aoqi@0: } aoqi@0: aoqi@0: if (end == start) { aoqi@0: throw new IllegalArgumentException("input is empty"); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: String uri; aoqi@0: String localPart; aoqi@0: String prefix; aoqi@0: aoqi@0: // search ':' aoqi@0: int idx = start + 1; // no point in searching the first char. that's not valid. aoqi@0: while (idx < end && text.charAt(idx) != ':') { aoqi@0: idx++; aoqi@0: } aoqi@0: aoqi@0: if (idx == end) { aoqi@0: uri = nsc.getNamespaceURI(""); aoqi@0: localPart = text.subSequence(start, end).toString(); aoqi@0: prefix = ""; aoqi@0: } else { aoqi@0: // Prefix exists, check everything aoqi@0: prefix = text.subSequence(start, idx).toString(); aoqi@0: localPart = text.subSequence(idx + 1, end).toString(); aoqi@0: uri = nsc.getNamespaceURI(prefix); aoqi@0: // uri can never be null according to javadoc, aoqi@0: // but some users reported that there are implementations that return null. aoqi@0: if (uri == null || uri.length() == 0) // crap. the NamespaceContext interface is broken. aoqi@0: // error: unbound prefix aoqi@0: { aoqi@0: throw new IllegalArgumentException("prefix " + prefix + " is not bound to a namespace"); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return new QName(uri, localPart, prefix); aoqi@0: } aoqi@0: aoqi@0: public static GregorianCalendar _parseDateTime(CharSequence s) { aoqi@0: String val = WhiteSpaceProcessor.trim(s).toString(); aoqi@0: return getDatatypeFactory().newXMLGregorianCalendar(val).toGregorianCalendar(); aoqi@0: } aoqi@0: aoqi@0: public static String _printDateTime(Calendar val) { aoqi@0: return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z", val); aoqi@0: } aoqi@0: aoqi@0: public static String _printDate(Calendar val) { aoqi@0: return CalendarFormatter.doFormat((new StringBuilder("%Y-%M-%D").append("%z")).toString(),val); aoqi@0: } aoqi@0: aoqi@0: public static String _printInt(int val) { aoqi@0: return String.valueOf(val); aoqi@0: } aoqi@0: aoqi@0: public static String _printLong(long val) { aoqi@0: return String.valueOf(val); aoqi@0: } aoqi@0: aoqi@0: public static String _printDecimal(BigDecimal val) { aoqi@0: return val.toPlainString(); aoqi@0: } aoqi@0: aoqi@0: public static String _printDouble(double v) { aoqi@0: if (Double.isNaN(v)) { aoqi@0: return "NaN"; aoqi@0: } aoqi@0: if (v == Double.POSITIVE_INFINITY) { aoqi@0: return "INF"; aoqi@0: } aoqi@0: if (v == Double.NEGATIVE_INFINITY) { aoqi@0: return "-INF"; aoqi@0: } aoqi@0: return String.valueOf(v); aoqi@0: } aoqi@0: aoqi@0: public static String _printQName(QName val, NamespaceContext nsc) { aoqi@0: // Double-check aoqi@0: String qname; aoqi@0: String prefix = nsc.getPrefix(val.getNamespaceURI()); aoqi@0: String localPart = val.getLocalPart(); aoqi@0: aoqi@0: if (prefix == null || prefix.length() == 0) { // be defensive aoqi@0: qname = localPart; aoqi@0: } else { aoqi@0: qname = prefix + ':' + localPart; aoqi@0: } aoqi@0: aoqi@0: return qname; aoqi@0: } aoqi@0: aoqi@0: // base64 decoder aoqi@0: private static final byte[] decodeMap = initDecodeMap(); aoqi@0: private static final byte PADDING = 127; aoqi@0: aoqi@0: private static byte[] initDecodeMap() { aoqi@0: byte[] map = new byte[128]; aoqi@0: int i; aoqi@0: for (i = 0; i < 128; i++) { aoqi@0: map[i] = -1; aoqi@0: } aoqi@0: aoqi@0: for (i = 'A'; i <= 'Z'; i++) { aoqi@0: map[i] = (byte) (i - 'A'); aoqi@0: } aoqi@0: for (i = 'a'; i <= 'z'; i++) { aoqi@0: map[i] = (byte) (i - 'a' + 26); aoqi@0: } aoqi@0: for (i = '0'; i <= '9'; i++) { aoqi@0: map[i] = (byte) (i - '0' + 52); aoqi@0: } aoqi@0: map['+'] = 62; aoqi@0: map['/'] = 63; aoqi@0: map['='] = PADDING; aoqi@0: aoqi@0: return map; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * computes the length of binary data speculatively. aoqi@0: * aoqi@0: *

aoqi@0: * Our requirement is to create byte[] of the exact length to store the binary data. aoqi@0: * If we do this in a straight-forward way, it takes two passes over the data. aoqi@0: * Experiments show that this is a non-trivial overhead (35% or so is spent on aoqi@0: * the first pass in calculating the length.) aoqi@0: * aoqi@0: *

aoqi@0: * So the approach here is that we compute the length speculatively, without looking aoqi@0: * at the whole contents. The obtained speculative value is never less than the aoqi@0: * actual length of the binary data, but it may be bigger. So if the speculation aoqi@0: * goes wrong, we'll pay the cost of reallocation and buffer copying. aoqi@0: * aoqi@0: *

aoqi@0: * If the base64 text is tightly packed with no indentation nor illegal char aoqi@0: * (like what most web services produce), then the speculation of this method aoqi@0: * will be correct, so we get the performance benefit. aoqi@0: */ aoqi@0: private static int guessLength(String text) { aoqi@0: final int len = text.length(); aoqi@0: aoqi@0: // compute the tail '=' chars aoqi@0: int j = len - 1; aoqi@0: for (; j >= 0; j--) { aoqi@0: byte code = decodeMap[text.charAt(j)]; aoqi@0: if (code == PADDING) { aoqi@0: continue; aoqi@0: } aoqi@0: if (code == -1) // most likely this base64 text is indented. go with the upper bound aoqi@0: { aoqi@0: return text.length() / 4 * 3; aoqi@0: } aoqi@0: break; aoqi@0: } aoqi@0: aoqi@0: j++; // text.charAt(j) is now at some base64 char, so +1 to make it the size aoqi@0: int padSize = len - j; aoqi@0: if (padSize > 2) // something is wrong with base64. be safe and go with the upper bound aoqi@0: { aoqi@0: return text.length() / 4 * 3; aoqi@0: } aoqi@0: aoqi@0: // so far this base64 looks like it's unindented tightly packed base64. aoqi@0: // take a chance and create an array with the expected size aoqi@0: return text.length() / 4 * 3 - padSize; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * @param text aoqi@0: * base64Binary data is likely to be long, and decoding requires aoqi@0: * each character to be accessed twice (once for counting length, another aoqi@0: * for decoding.) aoqi@0: * aoqi@0: * A benchmark showed that taking {@link String} is faster, presumably aoqi@0: * because JIT can inline a lot of string access (with data of 1K chars, it was twice as fast) aoqi@0: */ aoqi@0: public static byte[] _parseBase64Binary(String text) { aoqi@0: final int buflen = guessLength(text); aoqi@0: final byte[] out = new byte[buflen]; aoqi@0: int o = 0; aoqi@0: aoqi@0: final int len = text.length(); aoqi@0: int i; aoqi@0: aoqi@0: final byte[] quadruplet = new byte[4]; aoqi@0: int q = 0; aoqi@0: aoqi@0: // convert each quadruplet to three bytes. aoqi@0: for (i = 0; i < len; i++) { aoqi@0: char ch = text.charAt(i); aoqi@0: byte v = decodeMap[ch]; aoqi@0: aoqi@0: if (v != -1) { aoqi@0: quadruplet[q++] = v; aoqi@0: } aoqi@0: aoqi@0: if (q == 4) { aoqi@0: // quadruplet is now filled. aoqi@0: out[o++] = (byte) ((quadruplet[0] << 2) | (quadruplet[1] >> 4)); aoqi@0: if (quadruplet[2] != PADDING) { aoqi@0: out[o++] = (byte) ((quadruplet[1] << 4) | (quadruplet[2] >> 2)); aoqi@0: } aoqi@0: if (quadruplet[3] != PADDING) { aoqi@0: out[o++] = (byte) ((quadruplet[2] << 6) | (quadruplet[3])); aoqi@0: } aoqi@0: q = 0; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (buflen == o) // speculation worked out to be OK aoqi@0: { aoqi@0: return out; aoqi@0: } aoqi@0: aoqi@0: // we overestimated, so need to create a new buffer aoqi@0: byte[] nb = new byte[o]; aoqi@0: System.arraycopy(out, 0, nb, 0, o); aoqi@0: return nb; aoqi@0: } aoqi@0: private static final char[] encodeMap = initEncodeMap(); aoqi@0: aoqi@0: private static char[] initEncodeMap() { aoqi@0: char[] map = new char[64]; aoqi@0: int i; aoqi@0: for (i = 0; i < 26; i++) { aoqi@0: map[i] = (char) ('A' + i); aoqi@0: } aoqi@0: for (i = 26; i < 52; i++) { aoqi@0: map[i] = (char) ('a' + (i - 26)); aoqi@0: } aoqi@0: for (i = 52; i < 62; i++) { aoqi@0: map[i] = (char) ('0' + (i - 52)); aoqi@0: } aoqi@0: map[62] = '+'; aoqi@0: map[63] = '/'; aoqi@0: aoqi@0: return map; aoqi@0: } aoqi@0: aoqi@0: public static char encode(int i) { aoqi@0: return encodeMap[i & 0x3F]; aoqi@0: } aoqi@0: aoqi@0: public static byte encodeByte(int i) { aoqi@0: return (byte) encodeMap[i & 0x3F]; aoqi@0: } aoqi@0: aoqi@0: public static String _printBase64Binary(byte[] input) { aoqi@0: return _printBase64Binary(input, 0, input.length); aoqi@0: } aoqi@0: aoqi@0: public static String _printBase64Binary(byte[] input, int offset, int len) { aoqi@0: char[] buf = new char[((len + 2) / 3) * 4]; aoqi@0: int ptr = _printBase64Binary(input, offset, len, buf, 0); aoqi@0: assert ptr == buf.length; aoqi@0: return new String(buf); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Encodes a byte array into a char array by doing base64 encoding. aoqi@0: * aoqi@0: * The caller must supply a big enough buffer. aoqi@0: * aoqi@0: * @return aoqi@0: * the value of {@code ptr+((len+2)/3)*4}, which is the new offset aoqi@0: * in the output buffer where the further bytes should be placed. aoqi@0: */ aoqi@0: public static int _printBase64Binary(byte[] input, int offset, int len, char[] buf, int ptr) { aoqi@0: // encode elements until only 1 or 2 elements are left to encode aoqi@0: int remaining = len; aoqi@0: int i; aoqi@0: for (i = offset;remaining >= 3; remaining -= 3, i += 3) { aoqi@0: buf[ptr++] = encode(input[i] >> 2); aoqi@0: buf[ptr++] = encode( aoqi@0: ((input[i] & 0x3) << 4) aoqi@0: | ((input[i + 1] >> 4) & 0xF)); aoqi@0: buf[ptr++] = encode( aoqi@0: ((input[i + 1] & 0xF) << 2) aoqi@0: | ((input[i + 2] >> 6) & 0x3)); aoqi@0: buf[ptr++] = encode(input[i + 2] & 0x3F); aoqi@0: } aoqi@0: // encode when exactly 1 element (left) to encode aoqi@0: if (remaining == 1) { aoqi@0: buf[ptr++] = encode(input[i] >> 2); aoqi@0: buf[ptr++] = encode(((input[i]) & 0x3) << 4); aoqi@0: buf[ptr++] = '='; aoqi@0: buf[ptr++] = '='; aoqi@0: } aoqi@0: // encode when exactly 2 elements (left) to encode aoqi@0: if (remaining == 2) { aoqi@0: buf[ptr++] = encode(input[i] >> 2); aoqi@0: buf[ptr++] = encode(((input[i] & 0x3) << 4) aoqi@0: | ((input[i + 1] >> 4) & 0xF)); aoqi@0: buf[ptr++] = encode((input[i + 1] & 0xF) << 2); aoqi@0: buf[ptr++] = '='; aoqi@0: } aoqi@0: return ptr; aoqi@0: } aoqi@0: aoqi@0: public static void _printBase64Binary(byte[] input, int offset, int len, XMLStreamWriter output) throws XMLStreamException { aoqi@0: int remaining = len; aoqi@0: int i; aoqi@0: char[] buf = new char[4]; aoqi@0: aoqi@0: for (i = offset; remaining >= 3; remaining -= 3, i += 3) { aoqi@0: buf[0] = encode(input[i] >> 2); aoqi@0: buf[1] = encode( aoqi@0: ((input[i] & 0x3) << 4) aoqi@0: | ((input[i + 1] >> 4) & 0xF)); aoqi@0: buf[2] = encode( aoqi@0: ((input[i + 1] & 0xF) << 2) aoqi@0: | ((input[i + 2] >> 6) & 0x3)); aoqi@0: buf[3] = encode(input[i + 2] & 0x3F); aoqi@0: output.writeCharacters(buf, 0, 4); aoqi@0: } aoqi@0: // encode when exactly 1 element (left) to encode aoqi@0: if (remaining == 1) { aoqi@0: buf[0] = encode(input[i] >> 2); aoqi@0: buf[1] = encode(((input[i]) & 0x3) << 4); aoqi@0: buf[2] = '='; aoqi@0: buf[3] = '='; aoqi@0: output.writeCharacters(buf, 0, 4); aoqi@0: } aoqi@0: // encode when exactly 2 elements (left) to encode aoqi@0: if (remaining == 2) { aoqi@0: buf[0] = encode(input[i] >> 2); aoqi@0: buf[1] = encode(((input[i] & 0x3) << 4) aoqi@0: | ((input[i + 1] >> 4) & 0xF)); aoqi@0: buf[2] = encode((input[i + 1] & 0xF) << 2); aoqi@0: buf[3] = '='; aoqi@0: output.writeCharacters(buf, 0, 4); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Encodes a byte array into another byte array by first doing base64 encoding aoqi@0: * then encoding the result in ASCII. aoqi@0: * aoqi@0: * The caller must supply a big enough buffer. aoqi@0: * aoqi@0: * @return aoqi@0: * the value of {@code ptr+((len+2)/3)*4}, which is the new offset aoqi@0: * in the output buffer where the further bytes should be placed. aoqi@0: */ aoqi@0: public static int _printBase64Binary(byte[] input, int offset, int len, byte[] out, int ptr) { aoqi@0: byte[] buf = out; aoqi@0: int remaining = len; aoqi@0: int i; aoqi@0: for (i=offset; remaining >= 3; remaining -= 3, i += 3 ) { aoqi@0: buf[ptr++] = encodeByte(input[i]>>2); aoqi@0: buf[ptr++] = encodeByte( aoqi@0: ((input[i]&0x3)<<4) | aoqi@0: ((input[i+1]>>4)&0xF)); aoqi@0: buf[ptr++] = encodeByte( aoqi@0: ((input[i+1]&0xF)<<2)| aoqi@0: ((input[i+2]>>6)&0x3)); aoqi@0: buf[ptr++] = encodeByte(input[i+2]&0x3F); aoqi@0: } aoqi@0: // encode when exactly 1 element (left) to encode aoqi@0: if (remaining == 1) { aoqi@0: buf[ptr++] = encodeByte(input[i]>>2); aoqi@0: buf[ptr++] = encodeByte(((input[i])&0x3)<<4); aoqi@0: buf[ptr++] = '='; aoqi@0: buf[ptr++] = '='; aoqi@0: } aoqi@0: // encode when exactly 2 elements (left) to encode aoqi@0: if (remaining == 2) { aoqi@0: buf[ptr++] = encodeByte(input[i]>>2); aoqi@0: buf[ptr++] = encodeByte( aoqi@0: ((input[i]&0x3)<<4) | aoqi@0: ((input[i+1]>>4)&0xF)); aoqi@0: buf[ptr++] = encodeByte((input[i+1]&0xF)<<2); aoqi@0: buf[ptr++] = '='; aoqi@0: } aoqi@0: aoqi@0: return ptr; aoqi@0: } aoqi@0: aoqi@0: private static CharSequence removeOptionalPlus(CharSequence s) { aoqi@0: int len = s.length(); aoqi@0: aoqi@0: if (len <= 1 || s.charAt(0) != '+') { aoqi@0: return s; aoqi@0: } aoqi@0: aoqi@0: s = s.subSequence(1, len); aoqi@0: char ch = s.charAt(0); aoqi@0: if ('0' <= ch && ch <= '9') { aoqi@0: return s; aoqi@0: } aoqi@0: if ('.' == ch) { aoqi@0: return s; aoqi@0: } aoqi@0: aoqi@0: throw new NumberFormatException(); aoqi@0: } aoqi@0: aoqi@0: private static boolean isDigitOrPeriodOrSign(char ch) { aoqi@0: if ('0' <= ch && ch <= '9') { aoqi@0: return true; aoqi@0: } aoqi@0: if (ch == '+' || ch == '-' || ch == '.') { aoqi@0: return true; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private static final Map DF_CACHE = Collections.synchronizedMap(new WeakHashMap()); aoqi@0: aoqi@0: public static DatatypeFactory getDatatypeFactory() { aoqi@0: ClassLoader tccl = AccessController.doPrivileged(new PrivilegedAction() { aoqi@0: public ClassLoader run() { aoqi@0: return Thread.currentThread().getContextClassLoader(); aoqi@0: } aoqi@0: }); aoqi@0: DatatypeFactory df = DF_CACHE.get(tccl); aoqi@0: if (df == null) { aoqi@0: synchronized (DatatypeConverterImpl.class) { aoqi@0: df = DF_CACHE.get(tccl); aoqi@0: if (df == null) { // to prevent multiple initialization aoqi@0: try { aoqi@0: df = DatatypeFactory.newInstance(); aoqi@0: } catch (DatatypeConfigurationException e) { aoqi@0: throw new Error(Messages.FAILED_TO_INITIALE_DATATYPE_FACTORY.format(),e); aoqi@0: } aoqi@0: DF_CACHE.put(tccl, df); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return df; aoqi@0: } aoqi@0: aoqi@0: private static final class CalendarFormatter { aoqi@0: aoqi@0: public static String doFormat(String format, Calendar cal) throws IllegalArgumentException { aoqi@0: int fidx = 0; aoqi@0: int flen = format.length(); aoqi@0: StringBuilder buf = new StringBuilder(); aoqi@0: aoqi@0: while (fidx < flen) { aoqi@0: char fch = format.charAt(fidx++); aoqi@0: aoqi@0: if (fch != '%') { // not a meta character aoqi@0: buf.append(fch); aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: // seen meta character. we don't do error check against the format aoqi@0: switch (format.charAt(fidx++)) { aoqi@0: case 'Y': // year aoqi@0: formatYear(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 'M': // month aoqi@0: formatMonth(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 'D': // days aoqi@0: formatDays(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 'h': // hours aoqi@0: formatHours(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 'm': // minutes aoqi@0: formatMinutes(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 's': // parse seconds. aoqi@0: formatSeconds(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: case 'z': // time zone aoqi@0: formatTimeZone(cal, buf); aoqi@0: break; aoqi@0: aoqi@0: default: aoqi@0: // illegal meta character. impossible. aoqi@0: throw new InternalError(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return buf.toString(); aoqi@0: } aoqi@0: aoqi@0: private static void formatYear(Calendar cal, StringBuilder buf) { aoqi@0: int year = cal.get(Calendar.YEAR); aoqi@0: aoqi@0: String s; aoqi@0: if (year <= 0) // negative value aoqi@0: { aoqi@0: s = Integer.toString(1 - year); aoqi@0: } else // positive value aoqi@0: { aoqi@0: s = Integer.toString(year); aoqi@0: } aoqi@0: aoqi@0: while (s.length() < 4) { aoqi@0: s = '0' + s; aoqi@0: } aoqi@0: if (year <= 0) { aoqi@0: s = '-' + s; aoqi@0: } aoqi@0: aoqi@0: buf.append(s); aoqi@0: } aoqi@0: aoqi@0: private static void formatMonth(Calendar cal, StringBuilder buf) { aoqi@0: formatTwoDigits(cal.get(Calendar.MONTH) + 1, buf); aoqi@0: } aoqi@0: aoqi@0: private static void formatDays(Calendar cal, StringBuilder buf) { aoqi@0: formatTwoDigits(cal.get(Calendar.DAY_OF_MONTH), buf); aoqi@0: } aoqi@0: aoqi@0: private static void formatHours(Calendar cal, StringBuilder buf) { aoqi@0: formatTwoDigits(cal.get(Calendar.HOUR_OF_DAY), buf); aoqi@0: } aoqi@0: aoqi@0: private static void formatMinutes(Calendar cal, StringBuilder buf) { aoqi@0: formatTwoDigits(cal.get(Calendar.MINUTE), buf); aoqi@0: } aoqi@0: aoqi@0: private static void formatSeconds(Calendar cal, StringBuilder buf) { aoqi@0: formatTwoDigits(cal.get(Calendar.SECOND), buf); aoqi@0: if (cal.isSet(Calendar.MILLISECOND)) { // milliseconds aoqi@0: int n = cal.get(Calendar.MILLISECOND); aoqi@0: if (n != 0) { aoqi@0: String ms = Integer.toString(n); aoqi@0: while (ms.length() < 3) { aoqi@0: ms = '0' + ms; // left 0 paddings. aoqi@0: } aoqi@0: buf.append('.'); aoqi@0: buf.append(ms); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** formats time zone specifier. */ aoqi@0: private static void formatTimeZone(Calendar cal, StringBuilder buf) { aoqi@0: TimeZone tz = cal.getTimeZone(); aoqi@0: aoqi@0: if (tz == null) { aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: // otherwise print out normally. aoqi@0: int offset = tz.getOffset(cal.getTime().getTime()); aoqi@0: aoqi@0: if (offset == 0) { aoqi@0: buf.append('Z'); aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: if (offset >= 0) { aoqi@0: buf.append('+'); aoqi@0: } else { aoqi@0: buf.append('-'); aoqi@0: offset *= -1; aoqi@0: } aoqi@0: aoqi@0: offset /= 60 * 1000; // offset is in milli-seconds aoqi@0: aoqi@0: formatTwoDigits(offset / 60, buf); aoqi@0: buf.append(':'); aoqi@0: formatTwoDigits(offset % 60, buf); aoqi@0: } aoqi@0: aoqi@0: /** formats Integer into two-character-wide string. */ aoqi@0: private static void formatTwoDigits(int n, StringBuilder buf) { aoqi@0: // n is always non-negative. aoqi@0: if (n < 10) { aoqi@0: buf.append('0'); aoqi@0: } aoqi@0: buf.append(n); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // DEPRECATED METHODS, KEPT FOR JAXB1 GENERATED CLASSES COMPATIBILITY, WILL BE REMOVED IN FUTURE aoqi@0: aoqi@0: @Deprecated aoqi@0: public String parseString(String lexicalXSDString) { aoqi@0: return lexicalXSDString; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public BigInteger parseInteger(String lexicalXSDInteger) { aoqi@0: return _parseInteger(lexicalXSDInteger); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printInteger(BigInteger val) { aoqi@0: return _printInteger(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public int parseInt(String s) { aoqi@0: return _parseInt(s); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public long parseLong(String lexicalXSLong) { aoqi@0: return _parseLong(lexicalXSLong); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public short parseShort(String lexicalXSDShort) { aoqi@0: return _parseShort(lexicalXSDShort); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printShort(short val) { aoqi@0: return _printShort(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public BigDecimal parseDecimal(String content) { aoqi@0: return _parseDecimal(content); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public float parseFloat(String lexicalXSDFloat) { aoqi@0: return _parseFloat(lexicalXSDFloat); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printFloat(float v) { aoqi@0: return _printFloat(v); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public double parseDouble(String lexicalXSDDouble) { aoqi@0: return _parseDouble(lexicalXSDDouble); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public boolean parseBoolean(String lexicalXSDBoolean) { aoqi@0: Boolean b = _parseBoolean(lexicalXSDBoolean); aoqi@0: return (b == null) ? false : b.booleanValue(); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printBoolean(boolean val) { aoqi@0: return val ? "true" : "false"; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public byte parseByte(String lexicalXSDByte) { aoqi@0: return _parseByte(lexicalXSDByte); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printByte(byte val) { aoqi@0: return _printByte(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public QName parseQName(String lexicalXSDQName, NamespaceContext nsc) { aoqi@0: return _parseQName(lexicalXSDQName, nsc); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public Calendar parseDateTime(String lexicalXSDDateTime) { aoqi@0: return _parseDateTime(lexicalXSDDateTime); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printDateTime(Calendar val) { aoqi@0: return _printDateTime(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public byte[] parseBase64Binary(String lexicalXSDBase64Binary) { aoqi@0: return _parseBase64Binary(lexicalXSDBase64Binary); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public byte[] parseHexBinary(String s) { aoqi@0: final int len = s.length(); aoqi@0: aoqi@0: // "111" is not a valid hex encoding. aoqi@0: if (len % 2 != 0) { aoqi@0: throw new IllegalArgumentException("hexBinary needs to be even-length: " + s); aoqi@0: } aoqi@0: aoqi@0: byte[] out = new byte[len / 2]; aoqi@0: aoqi@0: for (int i = 0; i < len; i += 2) { aoqi@0: int h = hexToBin(s.charAt(i)); aoqi@0: int l = hexToBin(s.charAt(i + 1)); aoqi@0: if (h == -1 || l == -1) { aoqi@0: throw new IllegalArgumentException("contains illegal character for hexBinary: " + s); aoqi@0: } aoqi@0: aoqi@0: out[i / 2] = (byte) (h * 16 + l); aoqi@0: } aoqi@0: aoqi@0: return out; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: private static int hexToBin(char ch) { aoqi@0: if ('0' <= ch && ch <= '9') { aoqi@0: return ch - '0'; aoqi@0: } aoqi@0: if ('A' <= ch && ch <= 'F') { aoqi@0: return ch - 'A' + 10; aoqi@0: } aoqi@0: if ('a' <= ch && ch <= 'f') { aoqi@0: return ch - 'a' + 10; aoqi@0: } aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printHexBinary(byte[] data) { aoqi@0: StringBuilder r = new StringBuilder(data.length * 2); aoqi@0: for (byte b : data) { aoqi@0: r.append(hexCode[(b >> 4) & 0xF]); aoqi@0: r.append(hexCode[(b & 0xF)]); aoqi@0: } aoqi@0: return r.toString(); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public long parseUnsignedInt(String lexicalXSDUnsignedInt) { aoqi@0: return _parseLong(lexicalXSDUnsignedInt); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printUnsignedInt(long val) { aoqi@0: return _printLong(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public int parseUnsignedShort(String lexicalXSDUnsignedShort) { aoqi@0: return _parseInt(lexicalXSDUnsignedShort); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public Calendar parseTime(String lexicalXSDTime) { aoqi@0: return getDatatypeFactory().newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar(); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printTime(Calendar val) { aoqi@0: return CalendarFormatter.doFormat("%h:%m:%s%z", val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public Calendar parseDate(String lexicalXSDDate) { aoqi@0: return getDatatypeFactory().newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar(); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printDate(Calendar val) { aoqi@0: return _printDate(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String parseAnySimpleType(String lexicalXSDAnySimpleType) { aoqi@0: return lexicalXSDAnySimpleType; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printString(String val) { aoqi@0: return val; aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printInt(int val) { aoqi@0: return _printInt(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printLong(long val) { aoqi@0: return _printLong(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printDecimal(BigDecimal val) { aoqi@0: return _printDecimal(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printDouble(double v) { aoqi@0: return _printDouble(v); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printQName(QName val, NamespaceContext nsc) { aoqi@0: return _printQName(val, nsc); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printBase64Binary(byte[] val) { aoqi@0: return _printBase64Binary(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printUnsignedShort(int val) { aoqi@0: return String.valueOf(val); aoqi@0: } aoqi@0: aoqi@0: @Deprecated aoqi@0: public String printAnySimpleType(String val) { aoqi@0: return val; aoqi@0: } aoqi@0: aoqi@0: }