io.honeybadger.com.fasterxml.jackson.databind.ext.OptionalHandlerFactory Maven / Gradle / Ivy
package com.fasterxml.jackson.databind.ext;
import java.util.logging.Logger;
import java.util.logging.Level;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.ser.Serializers;
/**
* Helper class used for isolating details of handling optional+external types
* (javax.xml classes) from standard factories that offer them.
*
* 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.
*
* 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";
// // 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
Logger.getLogger(OptionalHandlerFactory.class.getName())
.log(Level.INFO, "Could not load DOM `Node` and/or `Document` classes: no DOM support");
}
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 static final Java7Support _jdk7Helper;
static {
Java7Support x = null;
try {
x = Java7Support.instance();
} catch (Throwable t) { }
_jdk7Helper = x;
}
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 (_jdk7Helper != null) {
JsonSerializer> ser = _jdk7Helper.getSerializerForJavaNioFilePath(rawType);
if (ser != null) {
return ser;
}
}
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 (_jdk7Helper != null) {
JsonDeserializer> deser = _jdk7Helper.getDeserializerForJavaNioFilePath(rawType);
if (deser != null) {
return deser;
}
}
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;
}
}