| 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; |
| } |
| } |