FileDocCategorySizeDatePackage
DatatypeConverterImpl.javaAPI DocJava SE 6 API28252Tue Jun 10 00:27:04 BST 2008javax.xml.bind

DatatypeConverterImpl

public final class DatatypeConverterImpl extends Object implements DatatypeConverterInterface
This class is the JAXB RI's default implementation of the {@link DatatypeConverterInterface}.

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

This class is responsible for whitespace normalization.

author
  • Ryan Shoemaker, Sun Microsystems, Inc.
version
$Revision$
since
JAXB2.1

Fields Summary
public static final DatatypeConverterInterface
theInstance
To avoid re-creating instances, we cache one instance.
private static final char[]
hexCode
private static final byte[]
decodeMap
private static final byte
PADDING
private static final char[]
encodeMap
private static final DatatypeFactory
datatypeFactory
Constructors Summary
protected DatatypeConverterImpl()


      
    
Methods Summary
public static byte[]_parseBase64Binary(java.lang.String text)

param
text base64Binary data is likely to be long, and decoding requires each character to be accessed twice (once for counting length, another for decoding.) A benchmark showed that taking {@link String} is faster, presumably because JIT can inline a lot of string access (with data of 1K chars, it was twice as fast)

        final int buflen = guessLength(text);
        final byte[] out = new byte[buflen];
        int o=0;

        final int len = text.length();
        int i;

        final byte[] quadruplet = new byte[4];
        int q=0;

        // convert each quadruplet to three bytes.
        for( i=0; i<len; i++ ) {
            char ch = text.charAt(i);
            byte v = decodeMap[ch];

            if( v!=-1 )
                quadruplet[q++] = v;

            if(q==4) {
                // quadruplet is now filled.
                out[o++] = (byte)((quadruplet[0]<<2)|(quadruplet[1]>>4));
                if( quadruplet[2]!=PADDING )
                    out[o++] = (byte)((quadruplet[1]<<4)|(quadruplet[2]>>2));
                if( quadruplet[3]!=PADDING )
                    out[o++] = (byte)((quadruplet[2]<<6)|(quadruplet[3]));
                q=0;
            }
        }

        if(buflen==o) // speculation worked out to be OK
            return out;

        // we overestimated, so need to create a new buffer
        byte[] nb = new byte[o];
        System.arraycopy(out,0,nb,0,o);
        return nb;
    
public static boolean_parseBoolean(java.lang.CharSequence literal)

        int i=0;
        int len = literal.length();
        char ch;
        do {
            ch = literal.charAt(i++);
        } while(WhiteSpaceProcessor.isWhiteSpace(ch) && i<len);

        // if we are strict about errors, check i==len. and report an error

        if( ch=='t" || ch=='1" )        return true;
        if( ch=='f" || ch=='0" )        return false;
        return false;
    
public static byte_parseByte(java.lang.CharSequence literal)

        return (byte)_parseInt(literal);
    
public static java.util.GregorianCalendar_parseDateTime(java.lang.CharSequence s)

        String val = WhiteSpaceProcessor.trim(s).toString();
        return datatypeFactory.newXMLGregorianCalendar(val).toGregorianCalendar();
    
public static java.math.BigDecimal_parseDecimal(java.lang.CharSequence content)

        content = WhiteSpaceProcessor.trim(content);

        return new BigDecimal(content.toString());

        // from purely XML Schema perspective,
        // this implementation has a problem, since
        // in xs:decimal "1.0" and "1" is equal whereas the above
        // code will return different values for those two forms.
        //
        // the code was originally using com.sun.msv.datatype.xsd.NumberType.load,
        // but a profiling showed that the process of normalizing "1.0" into "1"
        // could take non-trivial time.
        //
        // also, from the user's point of view, one might be surprised if
        // 1 (not 1.0) is returned from "1.000"
    
public static double_parseDouble(java.lang.CharSequence _val)

        String val = WhiteSpaceProcessor.trim(_val).toString();

        if(val.equals("NaN"))    return Double.NaN;
        if(val.equals("INF"))    return Double.POSITIVE_INFINITY;
        if(val.equals("-INF"))    return Double.NEGATIVE_INFINITY;

        if(val.length()==0
        || !isDigitOrPeriodOrSign(val.charAt(0))
        || !isDigitOrPeriodOrSign(val.charAt(val.length()-1)) )
            throw new NumberFormatException(val);


        // these screening process is necessary due to the wobble of Float.valueOf method
        return Double.parseDouble(val);
    
public static float_parseFloat(java.lang.CharSequence _val)

        String s = WhiteSpaceProcessor.trim(_val).toString();
        /* Incompatibilities of XML Schema's float "xfloat" and Java's float "jfloat"

            * jfloat.valueOf ignores leading and trailing whitespaces,
              whereas this is not allowed in xfloat.
            * jfloat.valueOf allows "float type suffix" (f, F) to be
              appended after float literal (e.g., 1.52e-2f), whereare
              this is not the case of xfloat.

            gray zone
            ---------
            * jfloat allows ".523". And there is no clear statement that mentions
              this case in xfloat. Although probably this is allowed.
            *
        */

        if(s.equals("NaN"))         return Float.NaN;
        if(s.equals("INF"))         return Float.POSITIVE_INFINITY;
        if(s.equals("-INF"))        return Float.NEGATIVE_INFINITY;

        if(s.length()==0
        || !isDigitOrPeriodOrSign(s.charAt(0))
        || !isDigitOrPeriodOrSign(s.charAt(s.length()-1)) )
            throw new NumberFormatException();

        // these screening process is necessary due to the wobble of Float.valueOf method
        return Float.parseFloat(s);
    
public static int_parseInt(java.lang.CharSequence s)
Faster but less robust String->int conversion. Note that:
  1. XML Schema allows '+', but {@link Integer#valueOf(String)} is not.
  2. XML Schema allows leading and trailing (but not in-between) whitespaces.. {@link Integer#valueOf(String)} doesn't allow any.

        int len = s.length();
        int sign = 1;

        int r = 0;

        for( int i=0; i<len; i++ ) {
            char ch = s.charAt(i);
            if(WhiteSpaceProcessor.isWhiteSpace(ch)) {
                // skip whitespace
            } else
            if('0"<=ch && ch<='9") {
                r = r*10 + (ch-'0");
            } else
            if(ch=='-") {
                sign = -1;
            } else
            if(ch=='+") {
                // noop
            } else
                throw new NumberFormatException("Not a number: "+s);
        }

        return r*sign;
    
public static java.math.BigInteger_parseInteger(java.lang.CharSequence s)

        return new BigInteger(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
    
public static long_parseLong(java.lang.CharSequence s)

        return Long.valueOf(removeOptionalPlus(WhiteSpaceProcessor.trim(s)).toString());
    
public static javax.xml.namespace.QName_parseQName(java.lang.CharSequence text, javax.xml.namespace.NamespaceContext nsc)

return
null if fails to convert.

        int length = text.length();

        // trim whitespace
        int start=0;
        while(start<length && WhiteSpaceProcessor.isWhiteSpace(text.charAt(start)))
            start++;

        int end = length;
        while(end>start && WhiteSpaceProcessor.isWhiteSpace(text.charAt(end-1)))
            end--;

        if(end==start)
            throw new IllegalArgumentException("input is empty");


        String uri;
        String localPart;
        String prefix;

        // search ':'
        int idx=start+1;    // no point in searching the first char. that's not valid.
        while(idx<end && text.charAt(idx)!=':" )
            idx++;

        if( idx==end ) {
            uri = nsc.getNamespaceURI("");
            localPart = text.subSequence(start,end).toString();
            prefix = "";
        } else {
            // Prefix exists, check everything
            prefix = text.subSequence(start,idx).toString();
            localPart = text.subSequence(idx+1,end).toString();
            uri = nsc.getNamespaceURI(prefix);
            // uri can never be null according to javadoc,
            // but some users reported that there are implementations that return null.
            if(uri==null || uri.length()==0) // crap. the NamespaceContext interface is broken.
                // error: unbound prefix
                throw new IllegalArgumentException("prefix "+prefix+" is not bound to a namespace");
        }

        return new QName(uri,localPart,prefix);
    
public static short_parseShort(java.lang.CharSequence s)

        return (short)_parseInt(s);
    
public static java.lang.String_printBase64Binary(byte[] input)

        return _printBase64Binary(input, 0, input.length);
    
public static java.lang.String_printBase64Binary(byte[] input, int offset, int len)

        char[] buf = new char[((len+2)/3)*4];
        int ptr = _printBase64Binary(input,offset,len,buf,0);
        assert ptr==buf.length;
        return new String(buf);
    
public static int_printBase64Binary(byte[] input, int offset, int len, char[] buf, int ptr)
Encodes a byte array into a char array by doing base64 encoding. The caller must supply a big enough buffer.

return
the value of {@code ptr+((len+2)/3)*4}, which is the new offset in the output buffer where the further bytes should be placed.

        for( int i=offset; i<len; i+=3 ) {
            switch( len-i ) {
            case 1:
                buf[ptr++] = encode(input[i]>>2);
                buf[ptr++] = encode(((input[i])&0x3)<<4);
                buf[ptr++] = '=";
                buf[ptr++] = '=";
                break;
            case 2:
                buf[ptr++] = encode(input[i]>>2);
                buf[ptr++] = encode(
                            ((input[i]&0x3)<<4) |
                            ((input[i+1]>>4)&0xF));
                buf[ptr++] = encode((input[i+1]&0xF)<<2);
                buf[ptr++] = '=";
                break;
            default:
                buf[ptr++] = encode(input[i]>>2);
                buf[ptr++] = encode(
                            ((input[i]&0x3)<<4) |
                            ((input[i+1]>>4)&0xF));
                buf[ptr++] = encode(
                            ((input[i+1]&0xF)<<2)|
                            ((input[i+2]>>6)&0x3));
                buf[ptr++] = encode(input[i+2]&0x3F);
                break;
            }
        }
        return ptr;
    
public static int_printBase64Binary(byte[] input, int offset, int len, byte[] out, int ptr)
Encodes a byte array into another byte array by first doing base64 encoding then encoding the result in ASCII. The caller must supply a big enough buffer.

return
the value of {@code ptr+((len+2)/3)*4}, which is the new offset in the output buffer where the further bytes should be placed.

        byte[] buf = out;
        int max = len+offset;
        for( int i=offset; i<max; i+=3 ) {
            switch( max-i ) {
            case 1:
                buf[ptr++] = encodeByte(input[i]>>2);
                buf[ptr++] = encodeByte(((input[i])&0x3)<<4);
                buf[ptr++] = '=";
                buf[ptr++] = '=";
                break;
            case 2:
                buf[ptr++] = encodeByte(input[i]>>2);
                buf[ptr++] = encodeByte(
                            ((input[i]&0x3)<<4) |
                            ((input[i+1]>>4)&0xF));
                buf[ptr++] = encodeByte((input[i+1]&0xF)<<2);
                buf[ptr++] = '=";
                break;
            default:
                buf[ptr++] = encodeByte(input[i]>>2);
                buf[ptr++] = encodeByte(
                            ((input[i]&0x3)<<4) |
                            ((input[i+1]>>4)&0xF));
                buf[ptr++] = encodeByte(
                            ((input[i+1]&0xF)<<2)|
                            ((input[i+2]>>6)&0x3));
                buf[ptr++] = encodeByte(input[i+2]&0x3F);
                break;
            }
        }

        return ptr;
    
public static java.lang.String_printBoolean(boolean val)

        return val?"true":"false";
    
public static java.lang.String_printByte(byte val)

        return String.valueOf(val);
    
public static java.lang.String_printDateTime(java.util.Calendar val)

        return CalendarFormatter.doFormat("%Y-%M-%DT%h:%m:%s%z",val);
    
public static java.lang.String_printDecimal(java.math.BigDecimal val)

        return val.toPlainString();
    
public static java.lang.String_printDouble(double v)

        if( v==Double.NaN )                    return "NaN";
        if( v==Double.POSITIVE_INFINITY )    return "INF";
        if( v==Double.NEGATIVE_INFINITY )    return "-INF";
        return String.valueOf(v);
    
public static java.lang.String_printFloat(float v)

        if( v==Float.NaN )                  return "NaN";
        if( v==Float.POSITIVE_INFINITY )    return "INF";
        if( v==Float.NEGATIVE_INFINITY )    return "-INF";
        return String.valueOf(v);
    
public static java.lang.String_printInt(int val)

        return String.valueOf(val);
    
public static java.lang.String_printInteger(java.math.BigInteger val)

        return val.toString();
    
public static java.lang.String_printLong(long val)

        return String.valueOf(val);
    
public static java.lang.String_printQName(javax.xml.namespace.QName val, javax.xml.namespace.NamespaceContext nsc)

        // Double-check
        String qname;
        String prefix = nsc.getPrefix( val.getNamespaceURI() );
        String localPart = val.getLocalPart();

        if( prefix == null || prefix.length()==0 ) { // be defensive
            qname = localPart;
        } else {
            qname = prefix + ':" + localPart;
        }

        return qname;
    
public static java.lang.String_printShort(short val)

        return String.valueOf(val);
    
public static charencode(int i)

        return encodeMap[i&0x3F];
    
public static byteencodeByte(int i)

        return (byte)encodeMap[i&0x3F];
    
private static intguessLength(java.lang.String text)
computes the length of binary data speculatively.

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

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

If the base64 text is tightly packed with no indentation nor illegal char (like what most web services produce), then the speculation of this method will be correct, so we get the performance benefit.

        final int len = text.length();

        // compute the tail '=' chars
        int j=len-1;
        for(; j>=0; j-- ) {
            byte code = decodeMap[text.charAt(j)];
            if(code==PADDING)
                continue;
            if(code==-1)
                // most likely this base64 text is indented. go with the upper bound
                return text.length()/4*3;
            break;
        }

        j++;    // text.charAt(j) is now at some base64 char, so +1 to make it the size
        int padSize = len-j;
        if(padSize >2) // something is wrong with base64. be safe and go with the upper bound
            return text.length()/4*3;

        // so far this base64 looks like it's unindented tightly packed base64.
        // take a chance and create an array with the expected size
        return text.length()/4*3-padSize;
    
private static inthexToBin(char ch)

        if( '0"<=ch && ch<='9" )    return ch-'0";
        if( 'A"<=ch && ch<='F" )    return ch-'A"+10;
        if( 'a"<=ch && ch<='f" )    return ch-'a"+10;
        return -1;
    
private static byte[]initDecodeMap()


        
        byte[] map = new byte[128];
        int i;
        for( i=0; i<128; i++ )        map[i] = -1;

        for( i='A"; i<='Z"; i++ )    map[i] = (byte)(i-'A");
        for( i='a"; i<='z"; i++ )    map[i] = (byte)(i-'a"+26);
        for( i='0"; i<='9"; i++ )    map[i] = (byte)(i-'0"+52);
        map['+"] = 62;
        map['/"] = 63;
        map['="] = PADDING;

        return map;
    
private static char[]initEncodeMap()


        
        char[] map = new char[64];
        int i;
        for( i= 0; i<26; i++ )        map[i] = (char)('A"+i);
        for( i=26; i<52; i++ )        map[i] = (char)('a"+(i-26));
        for( i=52; i<62; i++ )        map[i] = (char)('0"+(i-52));
        map[62] = '+";
        map[63] = '/";

        return map;
    
public static java.lang.StringinstallHook(java.lang.String s)
Just return the string passed as a parameter but installs an instance of this class as the DatatypeConverter implementation. Used from static fixed value initializers.

        DatatypeConverter.setDatatypeConverter(theInstance);
        return s;
    
private static booleanisDigitOrPeriodOrSign(char ch)

        if( '0"<=ch && ch<='9" )    return true;
        if( ch=='+" || ch=='-" || ch=='." )    return true;
        return false;
    
public java.lang.StringparseAnySimpleType(java.lang.String lexicalXSDAnySimpleType)

        return lexicalXSDAnySimpleType;
//        return (String)SimpleURType.theInstance._createValue( lexicalXSDAnySimpleType, null );
    
public byte[]parseBase64Binary(java.lang.String lexicalXSDBase64Binary)

        return _parseBase64Binary(lexicalXSDBase64Binary);
    
public booleanparseBoolean(java.lang.String lexicalXSDBoolean)

        return _parseBoolean(lexicalXSDBoolean);
    
public byteparseByte(java.lang.String lexicalXSDByte)

        return _parseByte(lexicalXSDByte);
    
public java.util.CalendarparseDate(java.lang.String lexicalXSDDate)

        return datatypeFactory.newXMLGregorianCalendar(lexicalXSDDate).toGregorianCalendar();
    
public java.util.CalendarparseDateTime(java.lang.String lexicalXSDDateTime)

        return _parseDateTime(lexicalXSDDateTime);
    
public java.math.BigDecimalparseDecimal(java.lang.String content)

        return _parseDecimal(content);
    
public doubleparseDouble(java.lang.String lexicalXSDDouble)

        return _parseDouble(lexicalXSDDouble);
    
public floatparseFloat(java.lang.String lexicalXSDFloat)

        return _parseFloat(lexicalXSDFloat);
    
public byte[]parseHexBinary(java.lang.String s)

        final int len = s.length();

        // "111" is not a valid hex encoding.
        if( len%2 != 0 )
            throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);

        byte[] out = new byte[len/2];

        for( int i=0; i<len; i+=2 ) {
            int h = hexToBin(s.charAt(i  ));
            int l = hexToBin(s.charAt(i+1));
            if( h==-1 || l==-1 )
                throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);

            out[i/2] = (byte)(h*16+l);
        }

        return out;
    
public intparseInt(java.lang.String s)

        return _parseInt(s);
    
public java.math.BigIntegerparseInteger(java.lang.String lexicalXSDInteger)

        return _parseInteger(lexicalXSDInteger);
    
public longparseLong(java.lang.String lexicalXSLong)

        return _parseLong(lexicalXSLong);
    
public javax.xml.namespace.QNameparseQName(java.lang.String lexicalXSDQName, javax.xml.namespace.NamespaceContext nsc)

        return _parseQName(lexicalXSDQName,nsc);
    
public shortparseShort(java.lang.String lexicalXSDShort)

        return _parseShort(lexicalXSDShort);
    
public java.lang.StringparseString(java.lang.String lexicalXSDString)

        return lexicalXSDString;
    
public java.util.CalendarparseTime(java.lang.String lexicalXSDTime)

        return datatypeFactory.newXMLGregorianCalendar(lexicalXSDTime).toGregorianCalendar();
    
public longparseUnsignedInt(java.lang.String lexicalXSDUnsignedInt)

        return _parseLong(lexicalXSDUnsignedInt);
    
public intparseUnsignedShort(java.lang.String lexicalXSDUnsignedShort)

        return _parseInt(lexicalXSDUnsignedShort);
    
public java.lang.StringprintAnySimpleType(java.lang.String val)

        return val;
    
public java.lang.StringprintBase64Binary(byte[] val)

        return _printBase64Binary(val);
    
public java.lang.StringprintBoolean(boolean val)

        return val?"true":"false";
    
public java.lang.StringprintByte(byte val)

        return _printByte(val);
    
public java.lang.StringprintDate(java.util.Calendar val)


        return CalendarFormatter.doFormat((new StringBuilder("%Y-%M-%D").append("%z")).toString(),val);
    
public java.lang.StringprintDateTime(java.util.Calendar val)

        return _printDateTime(val);
    
public java.lang.StringprintDecimal(java.math.BigDecimal val)

        return _printDecimal(val);
    
public java.lang.StringprintDouble(double v)

        return _printDouble(v);
    
public java.lang.StringprintFloat(float v)

        return _printFloat(v);
    
public java.lang.StringprintHexBinary(byte[] data)


        
        StringBuilder r = new StringBuilder(data.length*2);
        for ( byte b : data) {
            r.append(hexCode[(b >> 4) & 0xF]);
            r.append(hexCode[(b & 0xF)]);
        }
        return r.toString();
    
public java.lang.StringprintInt(int val)

        return _printInt(val);
    
public java.lang.StringprintInteger(java.math.BigInteger val)

        return _printInteger(val);
    
public java.lang.StringprintLong(long val)

        return _printLong(val);
    
public java.lang.StringprintQName(javax.xml.namespace.QName val, javax.xml.namespace.NamespaceContext nsc)

        return _printQName(val,nsc);
    
public java.lang.StringprintShort(short val)

        return _printShort(val);
    
public java.lang.StringprintString(java.lang.String val)

//        return StringType.theInstance.convertToLexicalValue( val, null );
        return val;
    
public java.lang.StringprintTime(java.util.Calendar val)

        return CalendarFormatter.doFormat("%h:%m:%s%z",val);
    
public java.lang.StringprintUnsignedInt(long val)

        return _printLong(val);
    
public java.lang.StringprintUnsignedShort(int val)

        return String.valueOf(val);
    
private static java.lang.CharSequenceremoveOptionalPlus(java.lang.CharSequence s)

        int len = s.length();

        if(len<=1 || s.charAt(0)!='+")    return s;

        s = s.subSequence(1,len);
        char ch = s.charAt(0);
        if('0"<=ch && ch<='9")    return s;
        if('."==ch )    return s;

        throw new NumberFormatException();