1 package com.fasterxml.jackson.databind.ext;
2
3 import java.util.logging.Logger;
4 import java.util.logging.Level;
5
6 import com.fasterxml.jackson.databind.*;
7 import com.fasterxml.jackson.databind.deser.Deserializers;
8 import com.fasterxml.jackson.databind.ser.Serializers;
9 import com.fasterxml.jackson.databind.util.ClassUtil;
10
11 /**
12  * Helper class used for isolating details of handling optional+external types
13  * (javax.xml classes) from standard factories that offer them.
14  *<p>
15  * Note that 2.7 changed handling to slightly less dynamic, to avoid having to
16  * traverse class hierarchy, which turned to be a performance issue in
17  * certain cases. Since DOM classes are assumed to exist on all Java 1.6
18  * environments (yes, even on Android/GAE), this part could be simplified by
19  * slightly less dynamic lookups.
20  *<p>
21  * Also with 2.7 we are supporting JDK 1.7/Java 7 type(s).
22  */

23 public class OptionalHandlerFactory implements java.io.Serializable
24 {
25     private static final long serialVersionUID = 1;
26
27     /* To make 2 main "optional" handler groups (javax.xml.stream)
28      * more dynamic, we better only figure out handlers completely dynamically, if and
29      * when they are needed. To do this we need to assume package prefixes.
30      */

31     private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml.";
32
33     private final static String SERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLSerializers";
34     private final static String DESERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLDeserializers";
35
36     // Plus we also have a single serializer for DOM Node:
37 //    private final static String CLASS_NAME_DOM_NODE = "org.w3c.dom.Node";
38 //    private final static String CLASS_NAME_DOM_DOCUMENT = "org.w3c.dom.Document";
39     private final static String SERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMSerializer";
40     private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer";
41     private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer";
42
43     // // Since 2.7, we will assume DOM classes are always found, both due to JDK 1.6 minimum
44     // // and because Android (and presumably GAE) have these classes
45
46     private final static Class<?> CLASS_DOM_NODE;
47     private final static Class<?> CLASS_DOM_DOCUMENT;
48
49     static {
50         Class<?> doc = null, node = null;
51         try {
52             node = org.w3c.dom.Node.class;
53             doc = org.w3c.dom.Document.class;
54         } catch (Throwable e) {
55             // not optimal but will do
56             Logger.getLogger(OptionalHandlerFactory.class.getName())
57                 .log(Level.INFO, "Could not load DOM `Node` and/or `Document` classes: no DOM support");
58         }
59         CLASS_DOM_NODE = node;
60         CLASS_DOM_DOCUMENT = doc;
61     }
62
63     // // But Java7 type(s) may or may not be; dynamic lookup should be fine, still
64     // // (note: also assume it comes from JDK so that ClassLoader issues with OSGi
65     // // can, I hope, be avoided?)
66
67     private static final Java7Handlers _jdk7Helper;
68     static {
69         Java7Handlers x = null;
70         try {
71             x = Java7Handlers.instance();
72         } catch (Throwable t) { }
73         _jdk7Helper = x;
74     }
75     
76     public final static OptionalHandlerFactory instance = new OptionalHandlerFactory();
77     
78     protected OptionalHandlerFactory() { }
79
80     /*
81     /**********************************************************
82     /* Public API
83     /**********************************************************
84      */

85     
86     public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type,
87             BeanDescription beanDesc)
88     {
89         final Class<?> rawType = type.getRawClass();
90
91         if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
92             return (JsonSerializer<?>) instantiate(SERIALIZER_FOR_DOM_NODE);
93         }
94
95         if (_jdk7Helper != null) {
96             JsonSerializer<?> ser = _jdk7Helper.getSerializerForJavaNioFilePath(rawType);
97             if (ser != null) {
98                 return ser;
99             }
100         }
101
102         String className = rawType.getName();
103         String factoryName;
104         if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML) || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
105             factoryName = SERIALIZERS_FOR_JAVAX_XML;
106         } else {
107             return null;
108         }
109
110         Object ob = instantiate(factoryName);
111         if (ob == null) { // could warn, if we had logging system (j.u.l?)
112             return null;
113         }
114         return ((Serializers) ob).findSerializer(config, type, beanDesc);
115     }
116
117     public JsonDeserializer<?> findDeserializer(JavaType type, DeserializationConfig config,
118             BeanDescription beanDesc)
119         throws JsonMappingException
120     {
121         final Class<?> rawType = type.getRawClass();
122
123         if (_jdk7Helper != null) {
124             JsonDeserializer<?> deser = _jdk7Helper.getDeserializerForJavaNioFilePath(rawType);
125             if (deser != null) {
126                 return deser;
127             }
128         }
129         if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(rawType)) {
130             return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_NODE);
131         }
132         if ((CLASS_DOM_DOCUMENT != null) && CLASS_DOM_DOCUMENT.isAssignableFrom(rawType)) {
133             return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_DOCUMENT);
134         }
135         String className = rawType.getName();
136         String factoryName;
137         if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
138                 || hasSuperClassStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
139             factoryName = DESERIALIZERS_FOR_JAVAX_XML;
140         } else {
141             return null;
142         }
143         Object ob = instantiate(factoryName);
144         if (ob == null) { // could warn, if we had logging system (j.u.l?)
145             return null;
146         }
147         return ((Deserializers) ob).findBeanDeserializer(type, config, beanDesc);
148     }
149
150     public boolean hasDeserializerFor(Class<?> valueType) {
151         if ((CLASS_DOM_NODE != null) && CLASS_DOM_NODE.isAssignableFrom(valueType)) {
152             return true;
153         }
154         if ((CLASS_DOM_DOCUMENT != null) && CLASS_DOM_DOCUMENT.isAssignableFrom(valueType)) {
155             return true;
156         }
157         String className = valueType.getName();
158         if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
159                 || hasSuperClassStartingWith(valueType, PACKAGE_PREFIX_JAVAX_XML)) {
160             return true;
161         }
162         return false;
163     }
164
165     /*
166     /**********************************************************
167     /* Internal helper methods
168     /**********************************************************
169      */

170
171     private Object instantiate(String className)
172     {
173         try {
174             return ClassUtil.createInstance(Class.forName(className), false);
175         } catch (LinkageError e) { }
176         // too many different kinds to enumerate here:
177         catch (Exception e) { }
178         return null;
179     }
180
181     /**
182      * Since 2.7 we only need to check for class extension, as all implemented
183      * types are classes, not interfaces. This has performance implications for
184      * some cases, as we do not need to go over interfaces implemented, just
185      * superclasses
186      * 
187      * @since 2.7
188      */

189     private boolean hasSuperClassStartingWith(Class<?> rawType, String prefix)
190     {
191         for (Class<?> supertype = rawType.getSuperclass(); supertype != null; supertype = supertype.getSuperclass()) {
192             if (supertype == Object.class) {
193                 return false;
194             }
195             if (supertype.getName().startsWith(prefix)) {
196                 return true;
197             }
198         }
199         return false;
200     }
201 }
202