blob: 7e880e3af87af72aa071329c0f16f60b4ecd73c0 [file] [log] [blame]
package com.fasterxml.jackson.databind.ext;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.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.
*<p>
* Note that 2.7 changed handling to slightly less dynamic, to avoid having to
* traverse class hierarchy, which turned to be a performance issue in
* certain cases. Since DOM classes are assumed to exist on all Java 1.6
* environments (yes, even on Android/GAE), this part could be simplified by
* slightly less dynamic lookups.
*<p>
* Also with 2.7 we are supporting JDK 1.7/Java 7 type(s).
*/
public class OptionalHandlerFactory implements java.io.Serializable
{
private static final long serialVersionUID = 1;
/* 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.";
private final static String SERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLSerializers";
private final static String DESERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLDeserializers";
// Plus we also have a single serializer for DOM Node:
// private final static String CLASS_NAME_DOM_NODE = "org.w3c.dom.Node";
// private final static String CLASS_NAME_DOM_DOCUMENT = "org.w3c.dom.Document";
private final static String SERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMSerializer";
private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer";
private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer";
private final static String DESERIALIZER_FOR_PATH = "com.fasterxml.jackson.databind.ext.PathDeserializer";
// // 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;
private final static Class<?> CLASS_DOM_DOCUMENT;
static {
Class<?> doc = null, node = null;
try {
node = org.w3c.dom.Node.class;
doc = org.w3c.dom.Document.class;
} catch (Exception e) {
// not optimal but will do
System.err.println("WARNING: could not load DOM Node and/or Document classes");
}
CLASS_DOM_NODE = node;
CLASS_DOM_DOCUMENT = doc;
}
// // But Java7 type(s) may or may not be; dynamic lookup should be fine, still
// // (note: also assume it comes from JDK so that ClassLoader issues with OSGi
// // can, I hope, be avoided?)
private final static Class<?> CLASS_JAVA7_PATH;
static {
Class<?> cls = null;
try {
cls = Class.forName("java.nio.file.Path");
} catch (Exception e) {
// not optimal but will do
System.err.println("WARNING: could not load Java7 Path class");
}
CLASS_JAVA7_PATH = cls;
}
public final static OptionalHandlerFactory instance = new OptionalHandlerFactory();
protected OptionalHandlerFactory() { }
/*
/**********************************************************
/* Public API
/**********************************************************
*/
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type,
BeanDescription beanDesc)
{
final Class<?> rawType = type.getRawClass();
if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) {
return ToStringSerializer.instance;
}
if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
return (JsonSerializer<?>) instantiate(SERIALIZER_FOR_DOM_NODE);
}
String className = rawType.getName();
String factoryName;
if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
factoryName = SERIALIZERS_FOR_JAVAX_XML;
} else {
return null;
}
Object ob = instantiate(factoryName);
if (ob == null) { // could warn, if we had logging system (j.u.l?)
return null;
}
return ((Serializers) ob).findSerializer(config, type, beanDesc);
}
public JsonDeserializer<?> findDeserializer(JavaType type, DeserializationConfig config,
BeanDescription beanDesc)
throws JsonMappingException
{
final Class<?> rawType = type.getRawClass();
if ((CLASS_JAVA7_PATH != null) && CLASS_JAVA7_PATH.isAssignableFrom(rawType)) {
return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_PATH);
}
if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_NODE);
}
if ((CLASS_DOM_DOCUMENT != null) && CLASS_DOM_DOCUMENT.isAssignableFrom(rawType)) {
return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_DOCUMENT);
}
String className = rawType.getName();
String factoryName;
if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
|| hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
factoryName = DESERIALIZERS_FOR_JAVAX_XML;
} else {
return null;
}
Object ob = instantiate(factoryName);
if (ob == null) { // could warn, if we had logging system (j.u.l?)
return null;
}
return ((Deserializers) ob).findBeanDeserializer(type, config, beanDesc);
}
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
private Object instantiate(String className)
{
try {
return Class.forName(className).newInstance();
} catch (LinkageError e) { }
// too many different kinds to enumerate here:
catch (Exception e) { }
return null;
}
/**
* 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
* superclasses
*
* @since 2.7
*/
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;
}
}