org.openanzo.rdf.datatype.TypeMaps.java Source code

Java tutorial

Introduction

Here is the source code for org.openanzo.rdf.datatype.TypeMaps.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2007-2008 IBM Corporation and Cambridge Semantics Incorporated.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/rdf/datatype/TypeMaps.java,v $
 * Created by:  Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>)
 * Created on: 10/23/06
 * Revision: $Id: TypeMaps.java 164 2007-07-31 14:11:09Z mroy $
 *
 * Contributors: IBM Corporation - initial API and implementation
 *     Cambridge Semantics Incorporated - Fork to Anzo
 *******************************************************************************/
package org.openanzo.rdf.datatype;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.DatatypeConverter;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import org.apache.commons.lang.time.DateUtils;
import org.apache.ws.jaxme.impl.DatatypeConverterImpl;
import org.openanzo.exceptions.AnzoRuntimeException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.rdf.MemURI;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.vocabulary.RDF;
import org.openanzo.rdf.vocabulary.XMLSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings("all")
/**
 * A singleton collection of {@link LexicalToNativeTypeMap}s and {@link NativeToLexicalTypeMap}s for converting between XML Schema types and Java classes.
 *
 * @author lee
 * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
 */
public class TypeMaps {

    private static final Logger log = LoggerFactory.getLogger(TypeMaps.class);

    /*
     * WARNING: JAXP doesn't mandate that DatatypeFactory implementations be thread-safe but all implementations
     * I've seen are indeed thread-safe on account of being stateless.
     *
     * Future JAXP edits might mandate thread safety since this bug has been accepted:
     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6466177
     */
    /** Datatype factory */
    public static final DatatypeFactory datatypeFactory;

    static {
        try {
            datatypeFactory = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException e) {
            throw new RuntimeException(e);
        }
    }

    private static boolean initialized = false;

    /**
     * This method should be called the constructor of each type map so that the DatatypeConverter is initialized. It is important to do this in every class
     * since we can't predict which will be loaded first. And the outer class isn't necessarily loaded when a static inner class is loaded. So putting this in
     * the outer class's static initializer isn't enough. See
     */
    private static void initializeDatatypeConverter() {
        if (!initialized) {
            DatatypeConverter.setDatatypeConverter(new DatatypeConverterImpl());
            initialized = true;
        }
    }

    /**
     * Creates a URI in the XSD namespace ending in <tt>base</tt>.
     * 
     * @param base
     * @return a URI in the XSD namespace ending in <tt>base</tt>.
     */
    static public String xsd(String base) {
        return XMLSchema.NAMESPACE + base;
    }

    private static final Date MIN_DATE = new Date(Long.MIN_VALUE);

    /**
     * Create an XML Calendar for the time in millis
     * 
     * @param millis
     * @return XML Calendar
     */
    public static XMLGregorianCalendar getXMLCaledar(long millis) {
        GregorianCalendar cal = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
        cal.clear();
        cal.setGregorianChange(MIN_DATE);
        cal.setTimeInMillis(millis);
        return TypeMaps.datatypeFactory.newXMLGregorianCalendar(cal);
    }

    /**
     * Create an XML Calendar for the time in millis
     * 
     * @param millis
     * @return XML Calendar
     */
    public static XMLGregorianCalendar getXMLCaledar(Date date) {
        GregorianCalendar cal = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
        cal.clear();
        cal.setGregorianChange(MIN_DATE);
        cal.setTime(date);
        return TypeMaps.datatypeFactory.newXMLGregorianCalendar(cal);
    }

    private TypeMaps() {
    }

    /**
     * Type map for string data.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMString implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {

        public TMString() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return String.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.STRING;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof String;
        }

        public String convertToLexicalValue(Object obj) {
            return (String) obj;
        }

        public URI getDatatype() {
            return XMLSchema.STRING;
        }

        public Class<?> getOutputJavaClass() {
            return String.class;
        }

        public Object convertToNativeObject(String value) {
            return value;
        }
    }

    /**
     * Type map for xsd:integer <--> BigInteger.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMInteger implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMInteger() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return BigInteger.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.INTEGER;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof BigInteger;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printInteger((BigInteger) obj);
        }

        public URI getDatatype() {
            return XMLSchema.INTEGER;
        }

        public Class<?> getOutputJavaClass() {
            return BigInteger.class;
        }

        public Object convertToNativeObject(String value) {
            // strip off leading +'s since our impl. doesn't handle them
            if (value.charAt(0) == '+')
                value = value.substring(1);
            return DatatypeConverter.parseInteger(value);
        }
    }

    /**
     * Type map for xsd:int <--> Integer.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMInt implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMInt() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Integer.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.INT;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Integer;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printInt((Integer) obj);
        }

        public URI getDatatype() {
            return XMLSchema.INT;
        }

        public Class<?> getOutputJavaClass() {
            return Integer.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseInt(value);
        }
    }

    /**
     * Type map for xsd:long <--> Long.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMLong implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMLong() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Long.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.LONG;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Long;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printLong((Long) obj);
        }

        public URI getDatatype() {
            return XMLSchema.LONG;
        }

        public Class<?> getOutputJavaClass() {
            return Long.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseLong(value);
        }
    }

    /**
     * Type map for xsd:short <--> Short.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMShort implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMShort() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Short.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.SHORT;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Short;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printShort((Short) obj);
        }

        public URI getDatatype() {
            return XMLSchema.SHORT;
        }

        public Class<?> getOutputJavaClass() {
            return Short.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseShort(value);
        }
    }

    /**
     * Type map for xsd:decimal <--> BigDecimal.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMDecimal implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMDecimal() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return BigDecimal.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DECIMAL;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof BigDecimal;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printDecimal((BigDecimal) obj);
        }

        public URI getDatatype() {
            return XMLSchema.DECIMAL;
        }

        public Class<?> getOutputJavaClass() {
            return BigDecimal.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseDecimal(value);
        }
    }

    /**
     * Type map for xsd:float <--> Float.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMFloat implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMFloat() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Float.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.FLOAT;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Float;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printFloat((Float) obj);
        }

        public URI getDatatype() {
            return XMLSchema.FLOAT;
        }

        public Class<?> getOutputJavaClass() {
            return Float.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseFloat(value);
        }
    }

    /**
     * Type map for xsd:double <--> Double.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMDouble implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMDouble() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Double.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DOUBLE;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Double;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printDouble((Double) obj);
        }

        public URI getDatatype() {
            return XMLSchema.DOUBLE;
        }

        public Class<?> getOutputJavaClass() {
            return Double.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseDouble(value);
        }
    }

    /**
     * Type map for xsd:boolean <--> Boolean.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMBoolean implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMBoolean() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Boolean.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.BOOLEAN;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Boolean;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printBoolean((Boolean) obj);
        }

        public URI getDatatype() {
            return XMLSchema.BOOLEAN;
        }

        public Class<?> getOutputJavaClass() {
            return Boolean.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseBoolean(value);
        }
    }

    /**
     * Type map for xsd:byte <--> Byte.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMByte implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMByte() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Byte.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.BYTE;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Byte;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printByte((Byte) obj);
        }

        public URI getDatatype() {
            return XMLSchema.BYTE;
        }

        public Class<?> getOutputJavaClass() {
            return Byte.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseByte(value);
        }
    }

    /**
     * Type map for xsd:base64Binary <--> byte[].
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMBase64Binary implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMBase64Binary() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return byte[].class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.BASE64BINARY;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof byte[];
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printBase64Binary((byte[]) obj);
        }

        public URI getDatatype() {
            return XMLSchema.BASE64BINARY;
        }

        public Class<?> getOutputJavaClass() {
            return byte[].class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseBase64Binary(value);
        }
    }

    /**
     * Type map for xsd:anyURI <--> org.openanzo.rdf.URI.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMAnyUri implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        public TMAnyUri() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return URI.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.ANYURI;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof URI;
        }

        public String convertToLexicalValue(Object obj) {
            return ((URI) obj).toString();
        }

        public URI getDatatype() {
            return XMLSchema.ANYURI;
        }

        public Class<?> getOutputJavaClass() {
            return URI.class;
        }

        public Object convertToNativeObject(String value) {
            return MemURI.create(value);
        }
    }

    /**
     * Type map for xsd:hexBinary --> byte[].
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMHexBinary implements LexicalToNativeTypeMap {
        public TMHexBinary() {
            initializeDatatypeConverter();
        }

        public URI getDatatype() {
            return XMLSchema.HEXBINARY;
        }

        public Class<?> getOutputJavaClass() {
            return byte[].class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseHexBinary(value);
        }
    }

    /**
     * Type map for xsd:unsignedInt --> Long.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMUnsignedInt implements LexicalToNativeTypeMap {
        public TMUnsignedInt() {
            initializeDatatypeConverter();
        }

        public URI getDatatype() {
            return XMLSchema.UNSIGNED_INT;
        }

        public Class<?> getOutputJavaClass() {
            return Long.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseUnsignedInt(value);
        }
    }

    /**
     * Type map for xsd:unsignedShort --> Integer.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMUnsignedShort implements LexicalToNativeTypeMap {
        public TMUnsignedShort() {
            initializeDatatypeConverter();
        }

        public URI getDatatype() {
            return XMLSchema.UNSIGNED_SHORT;
        }

        public Class<?> getOutputJavaClass() {
            return Integer.class;
        }

        public Object convertToNativeObject(String value) {
            return DatatypeConverter.parseUnsignedShort(value);
        }
    }

    /**
     * Type map for xsd:unsignedByte --> Short.
     * 
     * @author lee <lee@cambridgesemantics.com>
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMUnsignedByte implements LexicalToNativeTypeMap {
        public TMUnsignedByte() {
            initializeDatatypeConverter();
        }

        public URI getDatatype() {
            return XMLSchema.UNSIGNED_BYTE;
        }

        public Class<?> getOutputJavaClass() {
            return Short.class;
        }

        public Object convertToNativeObject(String value) {
            int val = DatatypeConverter.parseUnsignedShort(value);
            if (val >= 0 && val <= 255) {
                return val;
            } else {
                throw new NumberFormatException("Value out of range. Value:\"" + value);
            }
        }
    }

    /**
     * Maps java.util.Date --> xsd:dateTime using UTC as the time zone.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMJavaDate implements NativeToLexicalTypeMap {
        public TMJavaDate() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Date.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DATETIME;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            // Even though Timestamp, java.sql.Date, and java.sql.Time extend java.util.Date, their documentation
            // warns that they should not be treated as java.util.Date instances. The extension is used for
            // implementation inheritance rather than subtyping. We'll add a check here to prevent nasty issues
            // related to that. In particular, the most common issue is that the Timetamp's precision would
            // appear to drop from nanoseconds to seconds...not even milliseconds...seconds.
            return obj instanceof Date && !(obj instanceof Timestamp) && !(obj instanceof Time)
                    && !(obj instanceof java.sql.Date);
        }

        public String convertToLexicalValue(Object obj) {
            Calendar cal = Calendar.getInstance(DateUtils.UTC_TIME_ZONE);
            cal.setTime((Date) obj);
            return DatatypeConverter.printDateTime(cal);
        }
    }

    /**
     * Maps java.sql.Timestamp --> xsd:dateTime using UTC as the time zone and preserving the Timestamp's nanosecond precision.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMSQLTimestamp implements NativeToLexicalTypeMap {
        public TMSQLTimestamp() {
            initializeDatatypeConverter();
        }

        private static final Date MIN_DATE = new Date(Long.MIN_VALUE);

        public Class<?> getJavaClass() {
            return Timestamp.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DATETIME;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Timestamp;
        }

        public String convertToLexicalValue(Object obj) {
            Timestamp timestamp = (Timestamp) obj;
            // Truncate the time to seconds precision since we'll get the fractional seconds from the Timestamp#getNanos()
            // method below. Thus preventing loss of precision in the final lexical value.
            long wholeSecondTime = (timestamp.getTime() / 1000) * 1000;

            // It's annoyingly round-about to create a java.util.Calendar just to get an XMLGregorianCalendar.
            // This wouldn't be needed if you could create an XMLGregorianCalendar using 'milliseconds since the epoch'.
            // Oh well.
            GregorianCalendar cal = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
            cal.clear();
            cal.setGregorianChange(MIN_DATE);
            cal.setTimeInMillis(wholeSecondTime);

            XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
            int nanoseconds = timestamp.getNanos();
            String xmlval;
            if (nanoseconds > 0) {
                // The toXMLFormat() method of XMLGregorianCalendar can generate
                // an invalid string when using fractional seconds in JDK5 and JDK6. See
                // http://bugs.sun.com/view_bug.do?bug_id=2154623 which was originally filed as
                // http://bugs.sun.com/view_bug.do?bug_id=6608696.
                // The problem is that JDK5 changed the semantics of BigDecimal#toString() from underneath the
                // XMLGregorianCalendar code. So you can end up with fractional seconds in scientific notation (ex. 5E-9).
                // Looks like it will be fixed in JDK7 according to those bug reports.
                // So we write a workaround here to output the fractional seconds ourselves using
                // to correct BigDecimal#toPlainString() method.

                // First set the time zone to undefined so and the fractional seconds to null so that the string ends
                // string generated by the XMLGregorianCalendar will stop at the seconds field. We'll then
                // Add the fractional seconds string and the time zone string ourselves.
                xmlCal.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
                xmlCal.setFractionalSecond(null);
                BigDecimal fractionalSecond = BigDecimal.valueOf(timestamp.getNanos(), 9).stripTrailingZeros(); // nano is 10^-9
                String fractionalSecondStr = fractionalSecond.toPlainString(); // This will always be something like "0.043" so we get rid of the first character when appending to the XML string.
                xmlval = xmlCal.toXMLFormat() + fractionalSecondStr.substring(1) + "Z";
            } else if (nanoseconds == 0) {
                xmlCal.setFractionalSecond(null); // Doing this makes it so that no trailing zeroes are output.
                xmlval = xmlCal.toXMLFormat();
            } else {
                throw new AnzoRuntimeException(ExceptionConstants.IO.INVALID_DATETIME);
            }

            return xmlval;
        }
    }

    /**
     * Maps java.sql.Time --> xsd:time without any time zone.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMSQLTime implements NativeToLexicalTypeMap {
        public TMSQLTime() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Time.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.TIME;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Time;
        }

        public String convertToLexicalValue(Object obj) {
            return ((Time) obj).toString();
        }
    }

    /**
     * Maps java.sql.Date --> xsd:date without any time zone.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMSQLDate implements NativeToLexicalTypeMap {
        public TMSQLDate() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return java.sql.Date.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DATE;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof java.sql.Date;
        }

        public String convertToLexicalValue(Object obj) {
            return ((java.sql.Date) obj).toString();
        }
    }

    /**
     * Maps java.util.Calendar --> xsd:dateTime using UTC as the time zone and preserving the Timestamp's nanosecond precision.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMJavaCalendar implements NativeToLexicalTypeMap {
        public TMJavaCalendar() {
            initializeDatatypeConverter();
        }

        public Class<?> getJavaClass() {
            return Calendar.class;
        }

        public URI getOutputDatatype() {
            return XMLSchema.DATETIME;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            return obj instanceof Calendar;
        }

        public String convertToLexicalValue(Object obj) {
            return DatatypeConverter.printDateTime((Calendar) obj);
        }
    }

    /**
     * Generic type map for types that convert to and from XMLGregorianCalendar. In particular, the XML Schema types xsd:dateTime, xsd:date, xsd:time,
     * xsd:gYearMonth, xsd:gYear, xsd:gMonthDay, xsd:gDay, or xsd:gMonth.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMXMLGregorianCalendar implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {

        private final QName xsdType;

        private final URI xsdDatatypeUri;

        protected TMXMLGregorianCalendar(QName xsdType) {
            if (xsdType.equals(DatatypeConstants.DATETIME)) {
                xsdDatatypeUri = XMLSchema.DATETIME;
            } else if (xsdType.equals(DatatypeConstants.DATE)) {
                xsdDatatypeUri = XMLSchema.DATE;
            } else if (xsdType.equals(DatatypeConstants.TIME)) {
                xsdDatatypeUri = XMLSchema.TIME;
            } else if (xsdType.equals(DatatypeConstants.GDAY)) {
                xsdDatatypeUri = XMLSchema.GDAY;
            } else if (xsdType.equals(DatatypeConstants.GMONTH)) {
                xsdDatatypeUri = XMLSchema.GMONTH;
            } else if (xsdType.equals(DatatypeConstants.GMONTHDAY)) {
                xsdDatatypeUri = XMLSchema.GMONTHDAY;
            } else if (xsdType.equals(DatatypeConstants.GYEAR)) {
                xsdDatatypeUri = XMLSchema.GYEAR;
            } else if (xsdType.equals(DatatypeConstants.GYEARMONTH)) {
                xsdDatatypeUri = XMLSchema.GYEARMONTH;
            } else {
                throw new AnzoRuntimeException(ExceptionConstants.IO.UNSUPPORTED_DATATYPE,
                        "xsd:dateTime, xsd:date, xsd:time, xsd:gYearMonth, xsd:gYear, xsd:gMonthDay, xsd:gDay, or xsd:gMonth");
            }
            this.xsdType = xsdType;
        }

        public Class<?> getJavaClass() {
            return XMLGregorianCalendar.class;
        }

        public URI getOutputDatatype() {
            return xsdDatatypeUri;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            if (obj instanceof XMLGregorianCalendar) {
                try {
                    return ((XMLGregorianCalendar) obj).getXMLSchemaType().equals(xsdType);
                } catch (IllegalStateException e) {
                    // Thrown by XMLGregorianCalendar#getXMLSchemaType() if the object doesn't have the appropriate
                    // fields to be one of the XML Schema built-in types.
                    log.debug(LogUtils.GLITTER_MARKER, "Native value does not apply to this mapper.", e);
                }
            }
            return false;
        }

        public String convertToLexicalValue(Object obj) {
            return ((XMLGregorianCalendar) obj).toXMLFormat();
        }

        public URI getDatatype() {
            return xsdDatatypeUri;
        }

        public Class<?> getOutputJavaClass() {
            return XMLGregorianCalendar.class;
        }

        public Object convertToNativeObject(String value) {
            XMLGregorianCalendar cal = datatypeFactory.newXMLGregorianCalendar(value);
            if (cal.getXMLSchemaType().equals(xsdType)) {
                return cal;
            } else {
                throw new IllegalArgumentException();
            }
        }
    }

    /**
     * Generic type map for types that convert to and from javax.xml.datatype.Duration. In particular, the XML Schema types xsd:duration, xsd:yearMonthDuration,
     * and xsd:dayTimeDuration.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMDuration implements LexicalToNativeTypeMap, NativeToLexicalTypeMap {
        private final QName xsdType;

        private final URI xsdDatatypeUri;

        protected TMDuration(QName xsdType) {
            if (xsdType.equals(DatatypeConstants.DURATION)) {
                xsdDatatypeUri = XMLSchema.DURATION;
            } else if (xsdType.equals(DatatypeConstants.DURATION_DAYTIME)) {
                xsdDatatypeUri = XMLSchema.DURATION_DAYTIME;
            } else if (xsdType.equals(DatatypeConstants.DURATION_YEARMONTH)) {
                xsdDatatypeUri = XMLSchema.DURATION_YEARMONTH;
            } else {
                throw new AnzoRuntimeException(ExceptionConstants.IO.UNSUPPORTED_DATATYPE,
                        "xsd:duration, xsd:yearMonthDuration, or xsd:dayTimeDuration");
            }
            this.xsdType = xsdType;
        }

        public Class<?> getJavaClass() {
            return Duration.class;
        }

        public URI getOutputDatatype() {
            return xsdDatatypeUri;
        }

        public boolean canConvertToLexicalValue(Object obj) {
            if (obj instanceof Duration) {
                try {
                    return ((Duration) obj).getXMLSchemaType().equals(xsdType);
                } catch (IllegalStateException e) {
                    // Thrown by Duration#getXMLSchemaType() if the object doesn't have the appropriate
                    // fields to be one of the XML Schema built-in types.
                    log.debug(LogUtils.GLITTER_MARKER, "Native value does not apply to this mapper.", e);
                }
            }
            return false;
        }

        public String convertToLexicalValue(Object obj) {
            return ((Duration) obj).toString();
        }

        public URI getDatatype() {
            return xsdDatatypeUri;
        }

        public Class<?> getOutputJavaClass() {
            return Duration.class;
        }

        public Object convertToNativeObject(String value) {
            Duration duration = datatypeFactory.newDuration(value);
            if (DatatypeConstants.DURATION.equals(xsdType))
                return duration;
            try {
                if (duration.getXMLSchemaType().equals(xsdType)) {
                    return duration;
                } else {
                    throw new IllegalArgumentException();
                }
            } catch (IllegalStateException ise) {
                throw new IllegalArgumentException(ise);
            }
        }
    }

    /**
     * Type map for http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral --> String.
     * 
     * @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
     */
    static class TMXmlLiteralString implements LexicalToNativeTypeMap {
        public TMXmlLiteralString() {
            initializeDatatypeConverter();
        }

        public URI getDatatype() {
            return RDF.XMLLiteral;
        }

        public Class<?> getOutputJavaClass() {
            return String.class;
        }

        public Object convertToNativeObject(String value) {
            return value;
        }
    }
}