OptionalHandlerFactory.java

package tools.jackson.databind.ext;

import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import tools.jackson.databind.*;
import tools.jackson.databind.ext.sql.JavaSqlTypeHandlerFactory;
import tools.jackson.databind.ser.std.ToStringSerializer;

/**
 * Helper class used for isolating details of handling optional+external types
 * (javax.xml classes) from standard factories that offer them.
 */
public class OptionalHandlerFactory
{
    // To make 2 main "optional" handler groups (javax.xml.stream)
    // more dynamic, we better only figure out handlers completely dynamically, if and
    // when they are needed. To do this we need to assume package prefixes.
    private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml.";

    // // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum
    // // and because Android (and presumably GAE) have these classes

    private final static Class<?> CLASS_DOM_NODE = org.w3c.dom.Node.class;
    private final static Class<?> CLASS_DOM_DOCUMENT = org.w3c.dom.Document.class;

    public final static OptionalHandlerFactory instance = new OptionalHandlerFactory();

    protected OptionalHandlerFactory() {
    }

    /*
    /**********************************************************************
    /* Public API
    /**********************************************************************
     */

    public ValueSerializer<?> findSerializer(SerializationConfig config, JavaType type)
    {
        final Class<?> rawType = type.getRawClass();
        if (_IsXOfY(rawType, CLASS_DOM_NODE)) {
            return new DOMSerializer();
        }

        String className = rawType.getName();
        if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
                || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
            if (Duration.class.isAssignableFrom(rawType)) {
                return ToStringSerializer.instance;
            }
            if (QName.class.isAssignableFrom(rawType)) {
                return QNameSerializer.instance;
            }
            if (XMLGregorianCalendar.class.isAssignableFrom(rawType)) {
                return XMLGregorianCalendarSerializer.instance;
            }
        }
        return JavaSqlTypeHandlerFactory.instance.findSerializer(config, type);
    }

    public ValueDeserializer<?> findDeserializer(DeserializationConfig config, JavaType type)
    {
        final Class<?> rawType = type.getRawClass();
        if (_IsXOfY(rawType, CLASS_DOM_NODE)) {
            return new DOMDeserializer.NodeDeserializer();
        }
        if (_IsXOfY(rawType, CLASS_DOM_DOCUMENT)) {
            return new DOMDeserializer.DocumentDeserializer();
        }
        String className = rawType.getName();
        if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
                || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
            return CoreXMLDeserializers.findBeanDeserializer(config, type);
        }
        return JavaSqlTypeHandlerFactory.instance.findDeserializer(config, type);
    }

    public boolean hasDeserializerFor(Class<?> valueType) {
        if (_IsXOfY(valueType, CLASS_DOM_NODE)) {
            return true;
        }
        if (_IsXOfY(valueType, CLASS_DOM_DOCUMENT)) {
            return true;
        }
        String className = valueType.getName();

        if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
                || hasSuperClassStartingWith(valueType, PACKAGE_PREFIX_JAVAX_XML)) {
            return CoreXMLDeserializers.hasDeserializerFor(valueType);
        }
        return JavaSqlTypeHandlerFactory.instance.hasDeserializerFor(valueType);
    }

    private boolean _IsXOfY(Class<?> valueType, Class<?> expType) {
        return (expType != null) && expType.isAssignableFrom(valueType);
    }

    /*
    /**********************************************************************
    /* Internal helper methods
    /**********************************************************************
     */

    /**
     * Since 2.7 we only need to check for class extension, as all implemented
     * types are classes, not interfaces. This has performance implications for
     * some cases, as we do not need to go over interfaces implemented, just
     * super classes
     */
    private boolean hasSuperClassStartingWith(Class<?> rawType, String prefix)
    {
        for (Class<?> supertype = rawType.getSuperclass(); supertype != null; supertype = supertype.getSuperclass()) {
            if (supertype == Object.class) {
                return false;
            }
            if (supertype.getName().startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}