1 package com.fasterxml.jackson.dataformat.xml.util;
2
3 import javax.xml.namespace.QName;
4
5 import com.fasterxml.jackson.databind.*;
6 import com.fasterxml.jackson.databind.cfg.MapperConfig;
7 import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
8 import com.fasterxml.jackson.databind.type.ClassKey;
9 import com.fasterxml.jackson.databind.util.LRUMap;
10 import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;
11
12 /**
13  * Helper class used for efficiently finding root element name used with
14  * XML serializations.
15  */

16 public class XmlRootNameLookup
17     implements java.io.Serializable
18 {
19     private static final long serialVersionUID = 1L;
20
21     /**
22      * For efficient operation, let's try to minimize number of times we
23      * need to introspect root element name to use.
24      *<p>
25      * Note: changed to <code>transient</code> for 2.3; no point in serializing such
26      * state
27      */

28     protected final transient LRUMap<ClassKey,QName> _rootNames = new LRUMap<ClassKey,QName>(40, 200);
29
30     public XmlRootNameLookup() { }
31     
32     protected Object readResolve() {
33         // just need to make 100% sure it gets set to non-null, that's all
34         if (_rootNames == null) {
35             return new XmlRootNameLookup();
36         }
37         return this;
38     }
39
40     public QName findRootName(JavaType rootType, MapperConfig<?> config) {
41         return findRootName(rootType.getRawClass(), config);
42     }
43
44     public QName findRootName(Class<?> rootType, MapperConfig<?> config)
45     {
46         ClassKey key = new ClassKey(rootType);
47         QName name;
48         synchronized (_rootNames) {
49             name = _rootNames.get(key);
50         }
51         if (name != null) {
52             return name;
53         }
54         name = _findRootName(rootType, config);
55         synchronized (_rootNames) {
56             _rootNames.put(key, name);
57         }
58         return name;
59     }
60     
61     // NOTE: needed to be synchronized in 2.6.4, but 2.7.0 adds a proper fix
62     // for annotation introspection hence not needed any more
63     protected QName _findRootName(Class<?> rootType, MapperConfig<?> config)
64     {
65         BeanDescription beanDesc = config.introspectClassAnnotations(rootType);
66         AnnotationIntrospector intr = config.getAnnotationIntrospector();
67         AnnotatedClass ac = beanDesc.getClassInfo();
68         String localName = null;
69         String ns = null;
70
71         PropertyName root = intr.findRootName(ac);
72         if (root != null) {
73             localName = root.getSimpleName();
74             ns = root.getNamespace();
75         }
76         // No answer so far? Let's just default to using simple class name
77         if (localName == null || localName.length() == 0) {
78             // Should we strip out enclosing class tho? For now, nope:
79             // one caveat: array simple names end with "[]"; also, "$" needs replacing
80             localName = StaxUtil.sanitizeXmlTypeName(rootType.getSimpleName());
81             return new QName("", localName);
82         }
83         // Otherwise let's see if there's namespace, too (if we are missing it)
84         if (ns == null || ns.length() == 0) {
85             ns = findNamespace(intr, ac);
86         }
87         if (ns == null) { // some QName impls barf on nulls...
88             ns = "";
89         }
90         return new QName(ns, localName);
91     }
92
93     private String findNamespace(AnnotationIntrospector ai, AnnotatedClass ann)
94     {
95         for (AnnotationIntrospector intr : ai.allIntrospectors()) {
96             if (intr instanceof XmlAnnotationIntrospector) {
97                 String ns = ((XmlAnnotationIntrospector) intr).findNamespace(ann);
98                 if (ns != null) {
99                     return ns;
100                 }
101             }
102         }
103         return null;
104     }
105 }
106