package com.fasterxml.jackson.dataformat.xml.util;

import javax.xml.namespace.QName;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.type.ClassKey;
import com.fasterxml.jackson.databind.util.LRUMap;
import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;

Helper class used for efficiently finding root element name used with XML serializations.
/** * Helper class used for efficiently finding root element name used with * XML serializations. */
public class XmlRootNameLookup implements java.io.Serializable { private static final long serialVersionUID = 1L;
If all we get to serialize is a null, there's no way to figure out expected root name; so let's just default to literal "null".
/** * If all we get to serialize is a null, there's no way to figure out * expected root name; so let's just default to literal {@code "null"}. */
public final static QName ROOT_NAME_FOR_NULL = new QName("null");
For efficient operation, let's try to minimize number of times we need to introspect root element name to use.

Note: changed to transient for 2.3; no point in serializing such state

/** * For efficient operation, let's try to minimize number of times we * need to introspect root element name to use. *<p> * Note: changed to <code>transient</code> for 2.3; no point in serializing such * state */
protected final transient LRUMap<ClassKey,QName> _rootNames; public XmlRootNameLookup() { _rootNames = new LRUMap<ClassKey,QName>(40, 200); } protected Object readResolve() { // just need to make 100% sure it gets set to non-null, that's all // 05-Jan-2020, tatu: How is that possibly, you ask? JDK serialization, that's how // (it by-passes calls to constructors, as well as initializers) // ... and if you don't believe, try commenting it out and see test failure you get if (_rootNames == null) { return new XmlRootNameLookup(); } return this; } public QName findRootName(JavaType rootType, MapperConfig<?> config) { return findRootName(rootType.getRawClass(), config); } public QName findRootName(Class<?> rootType, MapperConfig<?> config) { ClassKey key = new ClassKey(rootType); QName name; synchronized (_rootNames) { name = _rootNames.get(key); } if (name != null) { return name; } name = _findRootName(rootType, config); synchronized (_rootNames) { _rootNames.put(key, name); } return name; } protected QName _findRootName(Class<?> rootType, MapperConfig<?> config) { BeanDescription beanDesc = config.introspectClassAnnotations(rootType); AnnotationIntrospector intr = config.getAnnotationIntrospector(); AnnotatedClass ac = beanDesc.getClassInfo(); String localName = null; String ns = null; PropertyName root = intr.findRootName(ac); if (root != null) { localName = root.getSimpleName(); ns = root.getNamespace(); } // No answer so far? Let's just default to using simple class name if (localName == null || localName.length() == 0) { // Should we strip out enclosing class tho? For now, nope: // one caveat: array simple names end with "[]"; also, "$" needs replacing localName = StaxUtil.sanitizeXmlTypeName(rootType.getSimpleName()); return _qname(ns, localName); } // Otherwise let's see if there's namespace, too (if we are missing it) if (ns == null || ns.isEmpty()) { ns = _findNamespace(intr, ac); } return _qname(ns, localName); } private QName _qname(String ns, String localName) { if (ns == null) { // some QName impls barf on nulls... ns = ""; } return new QName(ns, localName); } private String _findNamespace(AnnotationIntrospector ai, AnnotatedClass ann) { for (AnnotationIntrospector intr : ai.allIntrospectors()) { if (intr instanceof XmlAnnotationIntrospector) { String ns = ((XmlAnnotationIntrospector) intr).findNamespace(ann); if (ns != null) { return ns; } } } return null; } }