1 package com.fasterxml.jackson.module.jaxb;
2
3 import java.beans.Introspector;
4 import java.lang.annotation.Annotation;
5 import java.lang.reflect.*;
6 import java.util.*;
7
8 import javax.xml.bind.*;
9 import javax.xml.bind.annotation.*;
10 import javax.xml.bind.annotation.adapters.*;
11
12 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
13 import com.fasterxml.jackson.annotation.*;
14 import com.fasterxml.jackson.core.*;
15 import com.fasterxml.jackson.databind.*;
16 import com.fasterxml.jackson.databind.cfg.MapperConfig;
17 import com.fasterxml.jackson.databind.introspect.*;
18 import com.fasterxml.jackson.databind.jsontype.NamedType;
19 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
20 import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
21 import com.fasterxml.jackson.databind.type.TypeFactory;
22 import com.fasterxml.jackson.databind.util.BeanUtil;
23 import com.fasterxml.jackson.databind.util.ClassUtil;
24 import com.fasterxml.jackson.databind.util.Converter;
25 import com.fasterxml.jackson.module.jaxb.deser.DataHandlerJsonDeserializer;
26 import com.fasterxml.jackson.module.jaxb.ser.DataHandlerJsonSerializer;
27
28 /**
29  * Annotation introspector that leverages JAXB annotations where applicable to JSON mapping.
30  * As of Jackson 2.0, most JAXB annotations are supported at least to some degree.
31  * Ones that are NOT yet supported are:
32  * <ul>
33  * <li>{@link XmlAnyAttribute} not yet used (as of 1.5) but may be in future (as an alias for @JsonAnySetter?)
34  * <li>{@link XmlAnyElement} not yet used, may be as per [JACKSON-253]
35  * <li>{@link javax.xml.bind.annotation.XmlAttachmentRef}: JSON does not support external attachments
36  * <li>{@link XmlElementDecl}
37  * <li>{@link XmlElementRefs} because Jackson doesn't have any support for 'named' collection items -- however,
38  *    this may become partially supported as per [JACKSON-253].
39  * <li>{@link javax.xml.bind.annotation.XmlInlineBinaryData} since the underlying concepts
40  *    (like XOP) do not exist in JSON -- Jackson will always use inline base64 encoding as the method
41  * <li>{@link javax.xml.bind.annotation.XmlList} because JSON does not have (or necessarily need)
42  *    method of serializing list of values as space-separated Strings
43  * <li>{@link javax.xml.bind.annotation.XmlMimeType}
44  * <li>{@link javax.xml.bind.annotation.XmlMixed} since JSON has no concept of mixed content
45  * <li>{@link XmlRegistry}
46  * <li>{@link XmlSchema} not used, unlikely to be used
47  * <li>{@link XmlSchemaType} not used, unlikely to be used
48  * <li>{@link XmlSchemaTypes} not used, unlikely to be used
49  * <li>{@link XmlSeeAlso} not yet supported, but [ISSUE-1] filed to use it, so may be supported.
50  * </ul>
51  *
52  * Note also the following limitations:
53  *
54  * <ul>
55  * <li>Any property annotated with {@link XmlValue} will have implicit property named 'value' on
56  *    its JSON object; although (as of 2.4) it should be possible to override this name
57  *   </li>
58  * </ul>
59  *<p>
60  * A note on compatibility with Jackson XML module: since this module does not depend
61  * on Jackson XML module, it is bit difficult to make sure we will properly expose
62  * all information. But effort is made (as of version 2.3.3) to expose this information,
63  * even without using a specific sub-class from that project.
64  *
65  * @author Ryan Heaton
66  * @author Tatu Saloranta
67  */

68 public class JaxbAnnotationIntrospector
69     extends AnnotationIntrospector
70     implements Versioned
71 {
72     private static final long serialVersionUID = -1L;
73
74     protected final static String DEFAULT_NAME_FOR_XML_VALUE = "value";
75     
76     protected final static boolean DEFAULT_IGNORE_XMLIDREF = false;
77     
78     protected final static String MARKER_FOR_DEFAULT = "##default";
79
80     // @since 2.5
81     protected final static JsonFormat.Value FORMAT_STRING = new JsonFormat.Value().withShape(JsonFormat.Shape.STRING);
82
83     // @since 2.5
84     protected final static JsonFormat.Value FORMAT_INT = new JsonFormat.Value().withShape(JsonFormat.Shape.NUMBER_INT);
85
86     protected final String _jaxbPackageName;
87     protected final JsonSerializer<?> _dataHandlerSerializer;
88     protected final JsonDeserializer<?> _dataHandlerDeserializer;
89
90     protected final TypeFactory _typeFactory;
91     
92     protected final boolean _ignoreXmlIDREF;
93
94     /**
95      * When using {@link XmlValue} annotation, a placeholder name is assigned
96      * to property (unless overridden by explicit name); this configuration
97      * value specified what that name is.
98      */

99     protected String _xmlValueName = DEFAULT_NAME_FOR_XML_VALUE;
100
101     /**
102      * Inclusion value to return for properties annotated with 
103      * {@link XmlElement} and {@link XmlElementWrapper}, in case {@code nillable}
104      * property is left as {@code false}. Default setting is
105      * {@code null}; this is typically changed to either
106      * {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_NULL}
107      * or {@link com.fasterxml.jackson.annotation.JsonInclude.Include#NON_EMPTY}.
108      *
109      * @since 2.7
110      */

111     protected JsonInclude.Include _nonNillableInclusion = null;
112     
113     /**
114      * @deprecated Since 2.1, use constructor that takes TypeFactory.
115      */

116     @Deprecated
117     public JaxbAnnotationIntrospector() {
118         this(TypeFactory.defaultInstance());
119     }
120
121     public JaxbAnnotationIntrospector(MapperConfig<?> config) {
122         this(config.getTypeFactory());
123     }
124
125     public JaxbAnnotationIntrospector(TypeFactory typeFactory) {
126         this(typeFactory, DEFAULT_IGNORE_XMLIDREF);
127     }
128
129     /**
130      * @param typeFactory Type factory used for resolving type information
131      * @param ignoreXmlIDREF Whether {@link XmlIDREF} annotation should be processed
132      *   JAXB style (meaning that references are always serialized using id), or
133      *   not (first reference as full POJO, others as ids)
134      */

135     public JaxbAnnotationIntrospector(TypeFactory typeFactory, boolean ignoreXmlIDREF)
136     {
137         _typeFactory = (typeFactory == null)? TypeFactory.defaultInstance() : typeFactory;
138         _ignoreXmlIDREF = ignoreXmlIDREF;
139         _jaxbPackageName = XmlElement.class.getPackage().getName();
140
141         JsonSerializer<?> dataHandlerSerializer = null;
142         JsonDeserializer<?> dataHandlerDeserializer = null;
143         /* Data handlers included dynamically, to try to prevent issues on platforms
144          * with less than complete support for JAXB API
145          */

146         try {
147             dataHandlerSerializer = (JsonSerializer<?>) DataHandlerJsonSerializer.class.newInstance();
148             dataHandlerDeserializer = (JsonDeserializer<?>) DataHandlerJsonDeserializer.class.newInstance();
149         } catch (Throwable e) {
150             //dataHandlers not supported...
151         }
152         _dataHandlerSerializer = dataHandlerSerializer;
153         _dataHandlerDeserializer = dataHandlerDeserializer;
154     }
155
156     /**
157      * Method that will return version information stored in and read from jar
158      * that contains this class.
159      */

160     @Override
161     public Version version() {
162         return PackageVersion.VERSION;
163     }
164
165     /*
166     /**********************************************************
167     /* Configuration
168     /**********************************************************
169      */

170
171     /**
172      * Configuration method that can be used to change default name
173      * ("value") used for properties annotated with {@link XmlValue};
174      * note that setting it to <code>null</code> will actually avoid
175      * name override, and name will instead be derived from underlying
176      * method name using standard bean name introspection.
177      * 
178      * @since 2.5
179      */

180     public void setNameUsedForXmlValue(String name) {
181         _xmlValueName = name;
182     }
183
184     /**
185      * Accessor for getting currently configured placeholder named
186      * used for property annotated with {@link XmlValue}.
187      */

188     public String getNameUsedForXmlValue() {
189         return _xmlValueName;
190     }
191
192     /**
193      * Method to call to change inclusion criteria used for property annotated
194      * with {@link XmlElement} or {@link XmlElementWrapper}, with <code>nillable</code>
195      * set as <code>false</code>.
196      *
197      * @since 2.7
198      */

199     public JaxbAnnotationIntrospector setNonNillableInclusion(JsonInclude.Include incl) {
200         _nonNillableInclusion = incl;
201         return this;
202     }
203
204     /**
205      * @since 2.7
206      */

207     public JsonInclude.Include getNonNillableInclusion() {
208         return _nonNillableInclusion;
209     }
210
211     /*
212     /**********************************************************
213     /* Extended API (XmlAnnotationIntrospector)
214     /**********************************************************
215      */

216
217     // From XmlAnnotationIntrospector
218     // @Override
219     public String findNamespace(Annotated ann) {
220         String ns = null;
221         if (ann instanceof AnnotatedClass) {
222             // For classes, it must be @XmlRootElement. Also, we do
223             // want to use defaults from package, base class
224             XmlRootElement elem = findRootElementAnnotation((AnnotatedClass) ann);
225             if (elem != null) {
226                 ns = elem.namespace();
227             }
228         } else {
229             // For others, XmlElement or XmlAttribute work (anything else?)
230             XmlElement elem = findAnnotation(XmlElement.class, ann, falsefalsefalse);
231             if (elem != null) {
232                 ns = elem.namespace();
233             }
234             if (ns == null || MARKER_FOR_DEFAULT.equals(ns)) {
235                 XmlAttribute attr = findAnnotation(XmlAttribute.class, ann, falsefalsefalse);
236                 if (attr != null) {
237                     ns = attr.namespace();
238                 }
239             }
240         }
241         // JAXB uses marker for "not defined"
242         if (MARKER_FOR_DEFAULT.equals(ns)) {
243             ns = null;
244         }
245         return ns;
246     }
247
248     // From XmlAnnotationIntrospector
249     // @Override
250     /**
251      * Here we assume fairly simple logic; if there is <code>XmlAttribute</code> to be found,
252      * we consider it an attribute; if <code>XmlElement</code>, not-an-attribute; and otherwise
253      * we will consider there to be no information.
254      * Caller is likely to default to considering things as elements.
255      */

256     public Boolean isOutputAsAttribute(Annotated ann) {
257         XmlAttribute attr = findAnnotation(XmlAttribute.class, ann, falsefalsefalse);
258         if (attr != null) {
259             return Boolean.TRUE;
260         }
261         XmlElement elem = findAnnotation(XmlElement.class, ann, falsefalsefalse);
262         if (elem != null) {
263             return Boolean.FALSE;
264         }
265         return null;
266     }
267
268     // From XmlAnnotationIntrospector
269     // @Override
270     public Boolean isOutputAsText(Annotated ann) {
271         XmlValue attr = findAnnotation(XmlValue.class, ann, falsefalsefalse);
272         if (attr != null) {
273            return Boolean.TRUE;
274        }
275        return null;
276     }
277     
278     /*
279     /**********************************************************
280     /* General annotations (for classes, properties)
281     /**********************************************************
282      */

283     
284     @Override
285     public ObjectIdInfo findObjectIdInfo(Annotated ann)
286     {
287         /* To work in the way that works with JAXB and Jackson,
288          * we need to do things in bit of round-about way, starting
289          * with AnnotatedClass, locating @XmlID property, if any.
290          */

291         if (!(ann instanceof AnnotatedClass)) {
292             return null;
293         }
294         AnnotatedClass ac = (AnnotatedClass) ann;
295         /* Ideally, should not have to infer settings for class from
296          * individual fields and/or methods; but for now this
297          * has to do ...
298          */

299         PropertyName idPropName = null;
300
301         method_loop:
302         for (AnnotatedMethod m : ac.memberMethods()) {
303             XmlID idProp = m.getAnnotation(XmlID.class);
304             if (idProp == null) {
305                 continue;
306             }
307             switch (m.getParameterCount()) {
308             case 0: // getter
309                 idPropName = findJaxbPropertyName(m, m.getRawType(),
310                         BeanUtil.okNameForGetter(m, true));
311                 break method_loop;
312             case 1: // setter
313                 idPropName = findJaxbPropertyName(m, m.getRawType(),
314                         BeanUtil.okNameForMutator(m, "set"true));
315                 break method_loop;
316             }
317         }
318         if (idPropName == null) {
319             for (AnnotatedField f : ac.fields()) {
320                 XmlID idProp = f.getAnnotation(XmlID.class);
321                 if (idProp != null) {
322                     idPropName = findJaxbPropertyName(f, f.getRawType(), f.getName());
323                     break;
324                 }
325             }
326         }
327         if (idPropName != null) {
328             /* Scoping... hmmh. Could XML requires somewhat global scope, n'est pas?
329              * The alternative would be to use declared type of this class.
330              */

331             Class<?> scope = Object.class// alternatively would use 'ac.getRawType()'
332             // and we will assume that there exists property thus named...
333             return new ObjectIdInfo(idPropName,
334                     scope, ObjectIdGenerators.PropertyGenerator.class,
335                     // should we customize Object Id resolver somehow?
336                     SimpleObjectIdResolver.class);
337         }
338         
339         return null;
340     }
341
342     @Override
343     public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo base)
344     {
345         if (!_ignoreXmlIDREF) {
346             XmlIDREF idref = ann.getAnnotation(XmlIDREF.class);
347             /* JAXB makes XmlIDREF mean "always as id", as far as I know.
348              * May need to make it configurable in future, but for not that
349              * is fine...
350              */

351             if (idref != null) {
352                 if (base == null) {
353                     base = ObjectIdInfo.empty();
354                 }
355                 base = base.withAlwaysAsId(true);
356             }
357         }
358         return base;
359     }
360
361     /*
362     /**********************************************************
363     /* General class annotations
364     /**********************************************************
365      */

366
367     @Override
368     public PropertyName findRootName(AnnotatedClass ac)
369     {
370         XmlRootElement elem = findRootElementAnnotation(ac);
371         if (elem != null) {
372             return _combineNames(elem.name(), elem.namespace(), "");
373         }
374         return null;
375     }
376     
377     /*
378     @Override
379     public String[] findPropertiesToIgnore(Annotated a) {
380         // nothing in JAXB for this?
381         return null;
382     }
383     */

384
385     /* 08-Nov-2009, tatus: This is bit trickier: by default JAXB
386      * does actually ignore all unknown properties.
387      * But since there is no annotation to
388      * specify or change this, it seems wrong to claim such setting
389      * is in effect. May need to revisit this issue in future
390      */

391     /*
392     @Override
393     public Boolean findIgnoreUnknownProperties(AnnotatedClass ac);
394
395     @Override
396     public JsonIgnoreProperties.Value findPropertyIgnorals(Annotated ac);
397     */

398
399     @Override
400     public Boolean isIgnorableType(AnnotatedClass ac) {
401         // Does JAXB have any such indicators? No?
402         return null;
403     }
404
405     /*
406     /**********************************************************
407     /* General member (field, method/constructor) annotations
408     /**********************************************************
409      */

410
411     @Override
412     public boolean hasIgnoreMarker(AnnotatedMember m) {
413         return m.getAnnotation(XmlTransient.class) != null;
414     }
415
416     //(ryan) JAXB has @XmlAnyAttribute and @XmlAnyElement annotations, but they're not applicable in this case
417     // because JAXB says those annotations are only applicable to methods with specific signatures
418     // that Jackson doesn't support (Jackson's any setter needs 2 arguments, name and value, whereas
419     // JAXB expects use of Map
420
421     // 28-May-2016, tatu: While `@XmlAnyAttribute` looks ALMOST like applicable (esp.
422     //   assuming Jackson could use `Map` field, not just setter/getter), it is alas not.
423     //   The reason is that key is expected to be `QNmae`, XML/JAXB specific name and
424     //   something Jackson does not require or use
425     
426     /*
427     @Override
428     public boolean hasAnySetterAnnotation(AnnotatedMethod am) { }
429     
430     @Override
431     public boolean hasAnySetterAnnotation(AnnotatedMethod am)
432     */

433
434     @Override
435     public Boolean hasRequiredMarker(AnnotatedMember m) {
436         // 17-Oct-2017, tatu: [modules-base#32]
437         //   Before 2.9.3, was handling `true` correctly,
438         //   but otherwise used confusing logic (probably in attempt to try to avoid
439         //   reporting not-required for default value case
440         XmlAttribute attr = m.getAnnotation(XmlAttribute.class);
441         if (attr != null) {
442             return attr.required();
443         }
444         XmlElement elem = m.getAnnotation(XmlElement.class);
445         if (elem != null) {
446             return elem.required();
447         }
448         return null;
449     }
450
451     @Override
452     public PropertyName findWrapperName(Annotated ann)
453     {
454         XmlElementWrapper w = findAnnotation(XmlElementWrapper.class, ann, falsefalsefalse);
455         if (w != null) {
456             /* 18-Sep-2013, tatu: As per [jaxb-annotations#24], need to take special care with empty
457              *   String, as that should indicate here "use underlying unmodified
458              *   property name" (that is, one NOT overridden by @JsonProperty)
459              */

460             PropertyName name =  _combineNames(w.name(), w.namespace(), "");
461             // clumsy, yes, but has to do:
462             if (!name.hasSimpleName()) {
463                 if (ann instanceof AnnotatedMethod) {
464                     AnnotatedMethod am = (AnnotatedMethod) ann;
465                     String str;
466                     if (am.getParameterCount() == 0) {
467                         str = BeanUtil.okNameForGetter(am, true);
468                     } else {
469                         str = BeanUtil.okNameForMutator(am, "set"true);
470                     }
471                     if (str != null) {
472                         return name.withSimpleName(str);
473                     }
474                 }
475                 return name.withSimpleName(ann.getName());
476             }
477             return name;
478         }
479         return null;
480     }
481
482     // since 2.4
483     @Override
484     public String findImplicitPropertyName(AnnotatedMember m) {
485         XmlValue valueInfo = m.getAnnotation(XmlValue.class);
486         if (valueInfo != null) {
487             return _xmlValueName;
488         }
489         return null;
490     }
491
492     @Override
493     public JsonFormat.Value findFormat(Annotated m) {
494         /* [jaxb-annotations#33]: Use @XmlEnum value (Class) to indicate format,
495          *   iff it makes sense
496          */

497         if (m instanceof AnnotatedClass) {
498             XmlEnum ann = m.getAnnotation(XmlEnum.class);
499             if (ann != null) {
500                 Class<?> type = ann.value();
501                 if (type == String.class || type.isEnum()) {
502                     return FORMAT_STRING;
503                 }
504                 if (Number.class.isAssignableFrom(type)) {
505                     return FORMAT_INT;
506                 }
507             }
508         }
509         return null;
510     }
511     
512     /*
513     /**********************************************************
514     /* Property auto-detection
515     /**********************************************************
516      */

517     
518     @Override
519     public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
520         VisibilityChecker<?> checker)
521     {
522         XmlAccessType at = findAccessType(ac);
523         if (at == null) {
524             /* JAXB default is "PUBLIC_MEMBER"; however, here we should not
525              * override settings if there is no annotation -- that would mess
526              * up global baseline. Fortunately Jackson defaults are very close
527              * to JAXB 'PUBLIC_MEMBER' settings (considering that setters and
528              * getters must come in pairs)
529              */

530             return checker;
531         }
532         
533         // Note: JAXB does not do creator auto-detection, can (and should) ignore
534         switch (at) {
535         case FIELD: // all fields, independent of visibility; no methods
536             return checker.withFieldVisibility(Visibility.ANY)
537                 .withSetterVisibility(Visibility.NONE)
538                 .withGetterVisibility(Visibility.NONE)
539                 .withIsGetterVisibility(Visibility.NONE)
540                 ;
541         case NONE: // no auto-detection
542             return checker.withFieldVisibility(Visibility.NONE)
543             .withSetterVisibility(Visibility.NONE)
544             .withGetterVisibility(Visibility.NONE)
545             .withIsGetterVisibility(Visibility.NONE)
546             ;
547         case PROPERTY:
548             return checker.withFieldVisibility(Visibility.NONE)
549             .withSetterVisibility(Visibility.PUBLIC_ONLY)
550             .withGetterVisibility(Visibility.PUBLIC_ONLY)
551             .withIsGetterVisibility(Visibility.PUBLIC_ONLY)
552             ;
553         case PUBLIC_MEMBER:       
554             return checker.withFieldVisibility(Visibility.PUBLIC_ONLY)
555             .withSetterVisibility(Visibility.PUBLIC_ONLY)
556             .withGetterVisibility(Visibility.PUBLIC_ONLY)
557             .withIsGetterVisibility(Visibility.PUBLIC_ONLY)
558             ;
559         }
560         return checker;
561     }
562
563     /**
564      * Method for locating JAXB {@link XmlAccessType} annotation value
565      * for given annotated entity, if it has one, or inherits one from
566      * its ancestors (in JAXB sense, package etc). Returns null if
567      * nothing has been explicitly defined.
568      */

569     protected XmlAccessType findAccessType(Annotated ac)
570     {
571         XmlAccessorType at = findAnnotation(XmlAccessorType.class, ac, truetruetrue);
572         return (at == null) ? null : at.value();
573     }
574     
575     /*
576     /**********************************************************
577     /* Class annotations for PM type handling (1.5+)
578     /**********************************************************
579      */

580     
581     @Override
582     public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
583             AnnotatedClass ac, JavaType baseType)
584     {
585         // no per-class type resolvers, right?
586         return null;
587     }
588
589     @Override
590     public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
591             AnnotatedMember am, JavaType baseType)
592     {
593         /* First: @XmlElements and @XmlElementRefs only applies type for immediate property, if it
594          * is NOT a structured type.
595          */

596         if (baseType.isContainerType()) return null;
597         return _typeResolverFromXmlElements(am);
598     }
599
600     @Override
601     public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
602             AnnotatedMember am, JavaType containerType)
603     {
604         /* First: let's ensure property is a container type: caller should have
605          * verified but just to be sure
606          */

607         if (containerType.getContentType() == null) {
608             throw new IllegalArgumentException("Must call method with a container or reference type (got "+containerType+")");
609         }
610         return _typeResolverFromXmlElements(am);
611     }
612
613     protected TypeResolverBuilder<?> _typeResolverFromXmlElements(AnnotatedMember am)
614     {
615         /* If simple type, @XmlElements and @XmlElementRefs are applicable.
616          * Note: @XmlElement and @XmlElementRef are NOT handled here, since they
617          * are handled specifically as non-polymorphic indication
618          * of the actual type
619          */

620         XmlElements elems = findAnnotation(XmlElements.class, am, falsefalsefalse);
621         XmlElementRefs elemRefs = findAnnotation(XmlElementRefs.class, am, falsefalsefalse);
622         if (elems == null && elemRefs == null) {
623             return null;
624         }
625
626         TypeResolverBuilder<?> b = new StdTypeResolverBuilder();
627         // JAXB always uses type name as id
628         b = b.init(JsonTypeInfo.Id.NAME, null);
629         // and let's consider WRAPPER_OBJECT to be canonical inclusion method
630         b = b.inclusion(JsonTypeInfo.As.WRAPPER_OBJECT);
631         return b;        
632     }
633     
634     @Override
635     public List<NamedType> findSubtypes(Annotated a)
636     {
637         // No package/superclass defaulting (only used with fields, methods)
638         XmlElements elems = findAnnotation(XmlElements.class, a, falsefalsefalse);
639         ArrayList<NamedType> result = null;
640         if (elems != null) {
641             result = new ArrayList<NamedType>();
642             for (XmlElement elem : elems.value()) {
643                 String name = elem.name();
644                 if (MARKER_FOR_DEFAULT.equals(name)) name = null;
645                 result.add(new NamedType(elem.type(), name));
646             }
647         } else {
648             XmlElementRefs elemRefs = findAnnotation(XmlElementRefs.class, a, falsefalsefalse);
649             if (elemRefs != null) {
650                 result = new ArrayList<NamedType>();
651                 for (XmlElementRef elemRef : elemRefs.value()) {
652                     Class<?> refType = elemRef.type();
653                     // only good for types other than JAXBElement (which is XML based)
654                     if (!JAXBElement.class.isAssignableFrom(refType)) {
655                         // first consider explicit name declaration
656                         String name = elemRef.name();
657                         if (name == null || MARKER_FOR_DEFAULT.equals(name)) {
658                             XmlRootElement rootElement = (XmlRootElement) refType.getAnnotation(XmlRootElement.class);
659                             if (rootElement != null) {
660                                 name = rootElement.name();
661                             }
662                         }
663                         if (name == null || MARKER_FOR_DEFAULT.equals(name)) {
664                             name = Introspector.decapitalize(refType.getSimpleName());
665                         }
666                         result.add(new NamedType(refType, name));
667                     }
668                 }
669             }
670         }
671         
672         // Check @XmlSeeAlso as well.
673         /* 17-Aug-2012, tatu:  But wait! For structured type, what we really is
674          *    value (content) type!
675          *    If code below does not make full (or any) sense, do not despair -- it
676          *    is wrong. Yet it works. The call sequence before we get here is mangled,
677          *    its logic twisted... but as Dire Straits put it: "That ain't working --
678          *    that's The Way You Do It!"
679          */

680         XmlSeeAlso ann = a.getAnnotation(XmlSeeAlso.class);
681         if (ann != null) {
682             if (result == null) {
683                 result = new ArrayList<NamedType>();
684             }
685             for (Class<?> cls : ann.value()) {
686                 result.add(new NamedType(cls));
687             }
688         }
689         return result;
690     }
691
692     @Override
693     public String findTypeName(AnnotatedClass ac) {
694         XmlType type = findAnnotation(XmlType.class, ac, falsefalsefalse);
695         if (type != null) {
696             String name = type.name();
697             if (!MARKER_FOR_DEFAULT.equals(name)) return name;
698         }
699         return null;
700     }
701
702     /*
703     /**********************************************************
704     /* Serialization: general annotations
705     /**********************************************************
706      */

707
708     @Override
709     public JsonSerializer<?> findSerializer(Annotated am)
710     {
711         final Class<?> type = _rawSerializationType(am);
712
713         /*
714         // As per [JACKSON-722], more checks for structured types
715         XmlAdapter<Object,Object> adapter = findAdapter(am, true, type);
716         if (adapter != null) {
717             boolean fromClass = !(am instanceof AnnotatedMember);
718             // Ugh. Must match to see if adapter's bounded type (result to map to) matches property type
719             if (isContainerType(type)) {
720                 Class<?> bt = findAdapterBoundType(adapter);
721                 if (bt.isAssignableFrom(type)) {
722                     return new XmlAdapterJsonSerializer(adapter, fromClass);
723                 }
724                 // Note: if used for value type, handled in different place
725             } else {
726                 return new XmlAdapterJsonSerializer(adapter, fromClass);
727             }
728         }
729         */

730
731         // Add support for additional core XML types needed by JAXB
732         if (type != null) {
733             if (_dataHandlerSerializer != null && isDataHandler(type)) {
734                 return _dataHandlerSerializer;
735             }
736         }
737         return null;
738     }
739
740     /**
741      * Determines whether the type is assignable to class javax.activation.DataHandler without requiring that class
742      * to be on the classpath.
743      *
744      * @param type The type.
745      * @return Whether the type is assignable to class javax.activation.DataHandler
746      */

747     private boolean isDataHandler(Class<?> type)
748     {
749         return type != null && (Object.class != type)
750                && (("javax.activation.DataHandler".equals(type.getName()) || isDataHandler(type.getSuperclass())));
751     }
752
753     @Override
754     public Object findContentSerializer(Annotated a) {
755         return null;
756     }
757
758     @Override
759     @Deprecated // since 2.7
760     public Class<?> findSerializationType(Annotated a)
761     {
762         Class<?> allegedType = _getTypeFromXmlElement(a);
763         if (allegedType != null){
764             Class<?> rawPropType = _rawSerializationType(a);
765             if (!isContainerType(rawPropType)) {
766                 return allegedType;
767             }
768         }
769         return null;
770     }
771
772     @Override // @since 2.7
773     public JsonInclude.Value findPropertyInclusion(Annotated a)
774     {
775         JsonInclude.Include incl = _serializationInclusion(a, null);
776         if (incl == null) {
777             return JsonInclude.Value.empty();
778         }
779         return JsonInclude.Value.construct(incl, null);
780     }
781
782     JsonInclude.Include _serializationInclusion(Annotated a, JsonInclude.Include defValue)
783     {
784         XmlElementWrapper w = a.getAnnotation(XmlElementWrapper.class);
785         if (w != null) {
786             if (w.nillable()) {
787                 return JsonInclude.Include.ALWAYS;
788             }
789             // [jaxb-annotations#52]: Allow specifying inclusion for `nillable=false` too
790             if (_nonNillableInclusion != null) {
791                 return _nonNillableInclusion;
792             }
793         }
794         XmlElement e = a.getAnnotation(XmlElement.class);
795         if (e != null) {
796             if (e.nillable()) {
797                 return JsonInclude.Include.ALWAYS;
798             }
799             // [jaxb-annotations#52]: Allow specifying inclusion for `nillable=false` too
800             if (_nonNillableInclusion != null) {
801                 return _nonNillableInclusion;
802             }
803         }
804         //better pass default value through, if no explicit direction indicating otherwise
805         return defValue;
806     }
807
808     @Override // @since 2.7
809     public JavaType refineSerializationType(final MapperConfig<?> config,
810             final Annotated a, final JavaType baseType) throws JsonMappingException
811     {
812         Class<?> serClass = _getTypeFromXmlElement(a);
813         if (serClass == null) {
814             return baseType;
815         }
816         // First, JAXB has no annotations for key type, so can skip that part (wrt std annotations)
817         // But the meaning of main annotation(s) varies between container/non-container types
818         final TypeFactory tf = config.getTypeFactory();
819         if (baseType.getContentType() == null) { // non-container/-structured types, usually scalar:
820             // 27-Nov-2015, tatu: Since JAXB has just one annotation, must ignore it in
821             //   one direction, typically serialization (but not always):
822             if (!serClass.isAssignableFrom(baseType.getRawClass())) {
823                 return baseType;
824             }
825             if (baseType.hasRawClass(serClass)) {
826                 // 30-Nov-2015, tatu: As per [databind#1023], need to allow forcing of
827                 //    static typing this way
828                 return baseType.withStaticTyping();
829             }
830             try {
831                 return tf.constructGeneralizedType(baseType, serClass);
832             } catch (IllegalArgumentException iae) {
833                 throw new JsonMappingException(null,
834                         String.format("Failed to widen type %s with annotation (value %s), from '%s': %s",
835                                 baseType, serClass.getName(), a.getName(), iae.getMessage()),
836                                 iae);
837             }
838         } else {
839             // Otherwise, structured type:
840             JavaType contentType = baseType.getContentType();
841             if (contentType != null) { // collection[like], map[like], array, reference
842                 // as per earlier, may need to ignore annotation meant for deserialization
843                 if (!serClass.isAssignableFrom(contentType.getRawClass())) {
844                     return baseType;
845                 }
846                 // And then value types for all containers:
847                 if (contentType.hasRawClass(serClass)) {
848                      contentType = contentType.withStaticTyping();
849                 } else {
850                     try {
851                        contentType = tf.constructGeneralizedType(contentType, serClass);
852                     } catch (IllegalArgumentException iae) {
853                         throw new JsonMappingException(null,
854                                 String.format("Failed to widen value type of %s with concrete-type annotation (value %s), from '%s': %s",
855                                         baseType, serClass.getName(), a.getName(), iae.getMessage()),
856                                         iae);
857                     }
858                 }
859                 return baseType.withContentType(contentType);
860             }
861         }
862         return baseType;
863     }
864
865     /*
866     /**********************************************************
867     /* Serialization: class annotations
868     /**********************************************************
869      */

870
871     @Override
872     public String[] findSerializationPropertyOrder(AnnotatedClass ac)
873     {
874         // @XmlType.propOrder fits the bill here:
875         XmlType type = findAnnotation(XmlType.class, ac, truetruetrue);
876         if (type == null) {
877             return null;
878         }
879         String[] order = type.propOrder();
880         if (order == null || order.length == 0) {
881             return null;
882         }
883         return order;
884     }
885
886     @Override
887     public Boolean findSerializationSortAlphabetically(Annotated ann) {
888         return _findAlpha(ann);
889     }
890
891     private final Boolean _findAlpha(Annotated ann) {
892         XmlAccessorOrder order = findAnnotation(XmlAccessorOrder.class, ann, truetruetrue);
893         return (order == null) ? null : (order.value() == XmlAccessOrder.ALPHABETICAL);
894     }
895     
896     @Override
897     public Object findSerializationConverter(Annotated a)
898     {
899         Class<?> serType = _rawSerializationType(a);
900         // Can apply to both container and regular type; no difference yet here
901         XmlAdapter<?,?> adapter = findAdapter(a, true, serType);
902         if (adapter != null) {
903             return _converter(adapter, true);
904         }
905         return null;
906     }
907
908     @Override
909     public Object findSerializationContentConverter(AnnotatedMember a)
910     {
911         // But this one only applies to structured (container) types:
912         Class<?> serType = _rawSerializationType(a);
913         if (isContainerType(serType)) {
914             XmlAdapter<?,?> adapter = _findContentAdapter(a, true);
915             if (adapter != null) {
916                 return _converter(adapter, true);
917             }
918         }
919         return null;
920     }
921
922     /*
923     /**********************************************************
924     /* Serialization: property annotations
925     /**********************************************************
926      */

927
928     @Override
929     public PropertyName findNameForSerialization(Annotated a)
930     {
931         // 16-Sep-2016, tatu: Prior to 2.9 logic her more complicated, on assumption
932         //    that visibility rules may require return of "" if method/fied visible;
933         //    however, that is not required and causes issues so... now simpler:
934         if (a instanceof AnnotatedMethod) {
935             AnnotatedMethod am = (AnnotatedMethod) a;
936             return isVisible(am)
937                 ? findJaxbPropertyName(am, am.getRawType(), BeanUtil.okNameForGetter(am, true))
938                 : null;
939         }
940         if (a instanceof AnnotatedField) {
941             AnnotatedField af = (AnnotatedField) a;
942             return isVisible(af)
943                 ? findJaxbPropertyName(af, af.getRawType(), null)
944                 : null;
945         }
946         return null;
947     }
948
949     @Deprecated // since 2.9
950     @Override
951     public boolean hasAsValueAnnotation(AnnotatedMethod am) {
952         //since jaxb says @XmlValue can exist with attributes, this won't map as a JSON value.
953         return false;
954     }
955
956     // As per above, nothing to detect here either...?
957     /*
958     @Override
959     public Boolean findAsValueAnnotation(Annotated a) {
960     }
961     */

962
963     @Override // since 2.7
964     public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
965         HashMap<String,String> expl = null;
966         for (Field f : ClassUtil.getDeclaredFields(enumType)) {
967             if (!f.isEnumConstant()) {
968                 continue;
969             }
970             XmlEnumValue enumValue = f.getAnnotation(XmlEnumValue.class);
971             if (enumValue == null) {
972                 continue;
973             }
974             String n = enumValue.value();
975             if (n.isEmpty()) {
976                 continue;
977             }
978             if (expl == null) {
979                 expl = new HashMap<String,String>();
980             }
981             expl.put(f.getName(), n);
982         }
983         // and then stitch them together if and as necessary
984         if (expl != null) {
985             for (int i = 0, end = enumValues.length; i < end; ++i) {
986                 String defName = enumValues[i].name();
987                 String explValue = expl.get(defName);
988                 if (explValue != null) {
989                     names[i] = explValue;
990                 }
991             }
992         }
993         return names;
994     }
995
996     /*
997     /**********************************************************
998     /* Deserialization: general annotations
999     /**********************************************************
1000      */

1001     
1002     @Override
1003     public Object findDeserializer(Annotated am)
1004     {
1005         final Class<?> type = _rawDeserializationType(am);
1006
1007         /*
1008         // As per [JACKSON-722], more checks for structured types
1009         XmlAdapter<Object,Object> adapter = findAdapter(am, true, type);
1010         if (adapter != null) {
1011             // Ugh. Must match to see if adapter's bounded type (result to map to) matches property type
1012             if (isContainerType(type)) {
1013                 if (adapterTypeMatches(adapter, type)) {
1014                     return new XmlAdapterJsonDeserializer(adapter);
1015                 }
1016             } else {
1017                 return new XmlAdapterJsonDeserializer(adapter);
1018             }
1019         }
1020         */

1021
1022         // [JACKSON-150]: add support for additional core XML types needed by JAXB
1023         if (type != null) {
1024             if (_dataHandlerDeserializer != null && isDataHandler(type)) {
1025                 return _dataHandlerDeserializer;
1026             }
1027         }
1028
1029         return null;
1030     }
1031
1032     @Override
1033     public Object findKeyDeserializer(Annotated am) {
1034         // Is there something like this in JAXB?
1035         return null;
1036     }
1037
1038     @Override
1039     public Object findContentDeserializer(Annotated a) {
1040         return null;
1041     }
1042
1043     protected Class<?> _doFindDeserializationType(Annotated a, JavaType baseType)
1044     {
1045         /* @XmlJavaTypeAdapter will complicate handling of type information;
1046          * basically we better just ignore type we might find here altogether in that case
1047          */

1048         if (a.hasAnnotation(XmlJavaTypeAdapter.class)) {
1049             return null;
1050         }
1051         
1052         // false for classpackagesuper-class, since annotation can
1053         // only be attached to fields and methods
1054         XmlElement annotation = findAnnotation(XmlElement.class, a, falsefalsefalse);
1055         if (annotation != null) {
1056             Class<?> type = annotation.type();
1057             if (type != XmlElement.DEFAULT.class) {
1058                 return type;
1059             }
1060         }
1061         return null;
1062     }
1063
1064     // @since 2.7
1065     @Override
1066     public JavaType refineDeserializationType(final MapperConfig<?> config,
1067             final Annotated a, final JavaType baseType) throws JsonMappingException
1068     {
1069         Class<?> deserClass = _getTypeFromXmlElement(a);
1070         if (deserClass == null) {
1071             return baseType;
1072         }
1073
1074         final TypeFactory tf = config.getTypeFactory();
1075
1076         if (baseType.getContentType() == null) { // non-container/-structured types, usually scalar:
1077             if (baseType.hasRawClass(deserClass)) { // no change
1078                 return baseType;
1079             }
1080             // 27-Nov-2015, tatu: Since JAXB has just one annotation, must ignore it in
1081             //   one direction, typically serialization (but not always):
1082             if (!baseType.getRawClass().isAssignableFrom(deserClass)) {
1083                 return baseType;
1084             }
1085             try {
1086                 return tf.constructSpecializedType(baseType, deserClass);
1087             } catch (IllegalArgumentException iae) {
1088                 throw new JsonMappingException(null,
1089                         String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s",
1090                                 baseType, deserClass.getName(), a.getName(), iae.getMessage()),
1091                                 iae);
1092             }
1093         } else {
1094             // Otherwise, structured type:
1095             JavaType contentType = baseType.getContentType();
1096             if (contentType != null) { // collection[like], map[like], array, reference
1097                 // as per earlier, may need to ignore annotation meant for deserialization
1098                 if (!contentType.getRawClass().isAssignableFrom(deserClass)) {
1099                     return baseType;
1100                 }
1101                 // And then value types for all containers:
1102                 try {
1103                    contentType = tf.constructSpecializedType(contentType, deserClass);
1104                    return baseType.withContentType(contentType);
1105                 } catch (IllegalArgumentException iae) {
1106                     throw new JsonMappingException(null,
1107                             String.format("Failed to narrow type %s with annotation (value %s), from '%s': %s",
1108                                     baseType, deserClass.getName(), a.getName(), iae.getMessage()),
1109                                     iae);
1110                 }
1111             }
1112         }
1113         return baseType;
1114     }
1115
1116     /*
1117     /**********************************************************
1118     /* Deserialization: property annotations
1119     /**********************************************************
1120      */

1121
1122     @Override
1123     public PropertyName findNameForDeserialization(Annotated a)
1124     {
1125         // 16-Sep-2016, tatu: Prior to 2.9 logic her more complicated, on assumption
1126         //    that visibility rules may require return of "" if method/fied visible;
1127         //    however, that is not required and causes issues so... now simpler:
1128         if (a instanceof AnnotatedMethod) {
1129             AnnotatedMethod am = (AnnotatedMethod) a;
1130             if (!isVisible(am)) {
1131                 return null;
1132             }
1133             Class<?> rawType = am.getRawParameterType(0);
1134             return findJaxbPropertyName(am, rawType, BeanUtil.okNameForMutator(am, "set"true));
1135         }
1136         if (a instanceof AnnotatedField) {
1137             AnnotatedField af = (AnnotatedField) a;
1138             return isVisible(af)
1139                 ? findJaxbPropertyName(af, af.getRawType(), null)
1140                 : null;
1141         }
1142         return null;
1143     }
1144
1145     @Override
1146     public Object findDeserializationConverter(Annotated a)
1147     {
1148         // One limitation: for structured types this is done later on
1149         Class<?> deserType = _rawDeserializationType(a);
1150         if (isContainerType(deserType)) {
1151             XmlAdapter<?,?> adapter = findAdapter(a, true, deserType);
1152             if (adapter != null) {
1153                 return _converter(adapter, false);
1154             }
1155         } else {
1156             XmlAdapter<?,?> adapter = findAdapter(a, true, deserType);
1157             if (adapter != null) {
1158                 return _converter(adapter, false);
1159             }
1160         }
1161         return null;
1162     }
1163
1164     @Override
1165     public Object findDeserializationContentConverter(AnnotatedMember a)
1166     {
1167         // conversely, here we only apply this to container types:
1168         Class<?> deserType = _rawDeserializationType(a);
1169         if (isContainerType(deserType)) {
1170             XmlAdapter<?,?> adapter = _findContentAdapter(a, false);
1171             if (adapter != null) {
1172                 return _converter(adapter, false);
1173             }
1174         }
1175         return null;
1176     }    
1177     /*
1178     /**********************************************************
1179     /* Helper methods (non-API)
1180     /**********************************************************
1181      */

1182
1183     /**
1184      * Whether the specified field is invisible, per the JAXB visibility rules.
1185      *
1186      * @param f The field.
1187      * @return Whether the field is invisible.
1188      */

1189     private boolean isVisible(AnnotatedField f)
1190     {
1191         // TODO: use AnnotatedField's annotations directly
1192         for (Annotation annotation : f.getAnnotated().getDeclaredAnnotations()) {
1193             if (isJAXBAnnotation(annotation)) {
1194                 return true;
1195             }
1196         }
1197         XmlAccessType accessType = XmlAccessType.PUBLIC_MEMBER;
1198         XmlAccessorType at = findAnnotation(XmlAccessorType.class, f, truetruetrue);
1199         if (at != null) {
1200             accessType = at.value();
1201         }
1202         if (accessType == XmlAccessType.FIELD) {
1203             return true;
1204         }
1205         if (accessType == XmlAccessType.PUBLIC_MEMBER) {
1206             return Modifier.isPublic(f.getAnnotated().getModifiers());
1207         }
1208         return false;
1209     }
1210
1211     private boolean isVisible(AnnotatedMethod m)
1212     {
1213         // TODO: use AnnotatedField's annotations directly
1214         for (Annotation annotation : m.getAnnotated().getDeclaredAnnotations()) {
1215             if (isJAXBAnnotation(annotation)) {
1216                 return true;
1217             }
1218         }
1219         XmlAccessType accessType = XmlAccessType.PUBLIC_MEMBER;
1220         XmlAccessorType at = findAnnotation(XmlAccessorType.class, m, truetruetrue);
1221         if (at != null) {
1222             accessType = at.value();
1223         }
1224         if (accessType == XmlAccessType.PROPERTY || accessType == XmlAccessType.PUBLIC_MEMBER) {
1225             return Modifier.isPublic(m.getModifiers());
1226         }
1227         return false;
1228     }
1229     
1230     /**
1231      * Finds an annotation associated with given annotatable thing; or if
1232      * not found, a default annotation it may have (from super classpackage
1233      * and so on)
1234      *
1235      * @param annotationClass the annotation class.
1236      * @param annotated The annotated element.
1237      * @param includePackage Whether the annotation can be found on the package of the annotated element.
1238      * @param includeClass Whether the annotation can be found on the class of the annotated element.
1239      * @param includeSuperclasses Whether the annotation can be found on any superclasses of the class of the annotated element.
1240      * @return The annotation, or null if not found.
1241      */

1242     private <A extends Annotation> A findAnnotation(Class<A> annotationClass, Annotated annotated,
1243             boolean includePackage, boolean includeClass, boolean includeSuperclasses)
1244     {
1245         A annotation = annotated.getAnnotation(annotationClass);
1246         if (annotation != null) {
1247             return annotation;
1248         }
1249         Class<?> memberClass = null;
1250         /* 13-Feb-2011, tatu: [JACKSON-495] - need to handle AnnotatedParameter
1251          *   bit differently, since there is no JDK counterpart. We can still
1252          *   access annotations directly, just using different calls.
1253          */

1254         if (annotated instanceof AnnotatedParameter) {
1255             memberClass = ((AnnotatedParameter) annotated).getDeclaringClass();
1256         } else {
1257             AnnotatedElement annType = annotated.getAnnotated();
1258             if (annType instanceof Member) {
1259                 memberClass = ((Member) annType).getDeclaringClass();
1260                 if (includeClass) {
1261                     annotation = (A) memberClass.getAnnotation(annotationClass);
1262                     if (annotation != null) {
1263                         return annotation;
1264                     }
1265                 }
1266             } else if (annType instanceof Class<?>) {
1267                 memberClass = (Class<?>) annType;
1268
1269                 // 20-Oct-2017, tatu: as per [modules-base#31] we may get `VirtualAnnotatedMember`, should
1270                 //     not freak out
1271                 /*
1272             } else {
1273                 throw new IllegalStateException("Unsupported annotated member: " + annotated.getClass().getName());
1274 */

1275             }
1276         }
1277         if (memberClass != null) {
1278             if (includeSuperclasses) {
1279                 Class<?> superclass = memberClass.getSuperclass();
1280                 while (superclass != null && superclass != Object.class) {
1281                     annotation = (A) superclass.getAnnotation(annotationClass);
1282                     if (annotation != null) {
1283                         return annotation;
1284                     }
1285                     superclass = superclass.getSuperclass();
1286                 }
1287             }
1288             if (includePackage) {
1289                 Package pkg = memberClass.getPackage();
1290                 if (pkg != null) {
1291                     return memberClass.getPackage().getAnnotation(annotationClass);
1292                 }
1293             }
1294         }
1295         return null;
1296     }
1297
1298     /*
1299     /**********************************************************
1300     /* Helper methods for bean property introspection
1301     /**********************************************************
1302      */

1303
1304     /**
1305      * An annotation is handled if it's in the same package as @XmlElement, including subpackages.
1306      *
1307      * @param ann The annotation.
1308      * @return Whether the annotation is in the JAXB package.
1309      */

1310     protected boolean isJAXBAnnotation(Annotation ann)
1311     {
1312         /* note: class we want is the annotation class, not instance
1313          * (since annotation instances, like enums, may be of different
1314          * physical type!)
1315          */

1316         Class<?> cls = ann.annotationType();
1317         Package pkg = cls.getPackage();
1318         String pkgName = (pkg != null) ? pkg.getName() : cls.getName();
1319         if (pkgName.startsWith(_jaxbPackageName)) {
1320             return true;
1321         }
1322         return false;
1323     }
1324     
1325     private PropertyName findJaxbPropertyName(Annotated ae, Class<?> aeType, String defaultName)
1326     {
1327         XmlAttribute attribute = ae.getAnnotation(XmlAttribute.class);
1328         if (attribute != null) {
1329             return _combineNames(attribute.name(), attribute.namespace(), defaultName);
1330         }
1331         XmlElement element = ae.getAnnotation(XmlElement.class);
1332         if (element != null) {
1333             return _combineNames(element.name(), element.namespace(), defaultName);
1334         }
1335         XmlElementRef elementRef = ae.getAnnotation(XmlElementRef.class);
1336         boolean hasAName = (elementRef != null);
1337         if (hasAName) {
1338             if (!MARKER_FOR_DEFAULT.equals(elementRef.name())) {
1339                 return _combineNames(elementRef.name(), elementRef.namespace(), defaultName);
1340             }
1341             if (aeType != null) {
1342                 XmlRootElement rootElement = (XmlRootElement) aeType.getAnnotation(XmlRootElement.class);
1343                 if (rootElement != null) {
1344                     String name = rootElement.name();
1345                     if (!MARKER_FOR_DEFAULT.equals(name)) {
1346                         return _combineNames(name, rootElement.namespace(), defaultName);
1347                     }
1348                     // Is there a namespace there to use? Probably not?
1349                     return new PropertyName(Introspector.decapitalize(aeType.getSimpleName()));
1350                 }
1351             }
1352         }
1353         if (!hasAName) {
1354             hasAName = ae.hasAnnotation(XmlElementWrapper.class)
1355                     // [modules-base#44]: should consider this as implicit marker
1356                     || ae.hasAnnotation(XmlElements.class)
1357                     // 09-Aug-2014, tatu: Note: prior to 2.4.2, we used to give explicit name "value"
1358                     //   if there was "@XmlValue" annotation; since then, only implicit name.
1359                     || ae.hasAnnotation(XmlValue.class);
1360         }
1361         // One more thing: 
1362         return hasAName ? PropertyName.USE_DEFAULT : null;
1363     }
1364
1365     private static PropertyName _combineNames(String localName, String namespace,
1366             String defaultName)
1367     {
1368         if (MARKER_FOR_DEFAULT.equals(localName)) {
1369             if (MARKER_FOR_DEFAULT.equals(namespace)) {
1370                 return new PropertyName(defaultName);
1371             }
1372             return new PropertyName(defaultName, namespace);
1373         }
1374         if (MARKER_FOR_DEFAULT.equals(namespace)) {
1375             return new PropertyName(localName);
1376         }
1377         return new PropertyName(localName, namespace);
1378     }
1379     
1380     private XmlRootElement findRootElementAnnotation(AnnotatedClass ac)
1381     {
1382         // Yes, check package, no class (already included), yes superclasses
1383         return findAnnotation(XmlRootElement.class, ac, truefalsetrue);
1384     }
1385
1386     /**
1387      * Finds the XmlAdapter for the specified annotation.
1388      *
1389      * @param am The annotated element.
1390      * @param forSerialization If true, adapter for serialization; if falsefor deserialization
1391      * @param type
1392      * 
1393      * @return The adapter, or null if none.
1394      */

1395     private XmlAdapter<Object,Object> findAdapter(Annotated am, boolean forSerialization,
1396             Class<?> type)
1397     {
1398         // First of all, are we looking for annotations for class?
1399         if (am instanceof AnnotatedClass) {
1400             return findAdapterForClass((AnnotatedClass) am, forSerialization);
1401         }
1402         // Otherwise for a member. First, let's figure out type of property
1403         XmlJavaTypeAdapter adapterInfo = findAnnotation(XmlJavaTypeAdapter.class, am, truefalsefalse);
1404         if (adapterInfo != null) {
1405             XmlAdapter<Object,Object> adapter = checkAdapter(adapterInfo, type, forSerialization);
1406             if (adapter != null) {
1407                 return adapter;
1408             }
1409         }
1410         XmlJavaTypeAdapters adapters = findAnnotation(XmlJavaTypeAdapters.class, am, truefalsefalse);
1411         if (adapters != null) {
1412             for (XmlJavaTypeAdapter info : adapters.value()) {
1413                 XmlAdapter<Object,Object> adapter = checkAdapter(info, type, forSerialization);
1414                 if (adapter != null) {
1415                     return adapter;
1416                 }
1417             }
1418         }
1419         return null;
1420     }
1421     
1422     @SuppressWarnings("unchecked")
1423     private final XmlAdapter<Object,Object> checkAdapter(XmlJavaTypeAdapter adapterInfo, Class<?> typeNeeded,
1424             boolean forSerialization)
1425     {
1426         // if annotation has no type, it's applicable; if it has, must match
1427         Class<?> adaptedType = adapterInfo.type();
1428         
1429         if (adaptedType == XmlJavaTypeAdapter.DEFAULT.class) {
1430             JavaType type = _typeFactory.constructType(adapterInfo.value());
1431             JavaType[] params = _typeFactory.findTypeParameters(type, XmlAdapter.class);
1432             adaptedType = params[1].getRawClass();
1433         }
1434         if (adaptedType.isAssignableFrom(typeNeeded)) {
1435             @SuppressWarnings("rawtypes")
1436             Class<? extends XmlAdapter> cls = adapterInfo.value();
1437             // true -> yes, force access if need be
1438             return ClassUtil.createInstance(cls, true);
1439         }
1440         return null;
1441     }
1442     
1443     @SuppressWarnings("unchecked")
1444     private XmlAdapter<Object,Object> findAdapterForClass(AnnotatedClass ac, boolean forSerialization)
1445     {
1446         /* As per [JACKSON-411], XmlJavaTypeAdapter should not be inherited from super-class.
1447          * It would still be nice to be able to use mix-ins; but unfortunately we seem to lose
1448          * knowledge of class that actually declared the annotation. Thus, we'll only accept
1449          * declaration from specific class itself.
1450          */

1451         XmlJavaTypeAdapter adapterInfo = ac.getAnnotated().getAnnotation(XmlJavaTypeAdapter.class);
1452         if (adapterInfo != null) { // should we try caching this?
1453             @SuppressWarnings("rawtypes")
1454             Class<? extends XmlAdapter> cls = adapterInfo.value();
1455             // true -> yes, force access if need be
1456             return ClassUtil.createInstance(cls, true);
1457         }
1458         return null;
1459     }
1460
1461     protected final TypeFactory getTypeFactory() {
1462         return _typeFactory;
1463     }
1464     
1465     /**
1466      * Helper method used to distinguish structured types (arrays, Lists, Maps),
1467      * which with JAXB use different rules for defining content types.
1468      */

1469     private boolean isContainerType(Class<?> raw)
1470     {
1471         return raw.isArray() || Collection.class.isAssignableFrom(raw)
1472             || Map.class.isAssignableFrom(raw);
1473     }
1474
1475     private boolean adapterTypeMatches(XmlAdapter<?,?> adapter, Class<?> targetType)
1476     {
1477         return findAdapterBoundType(adapter).isAssignableFrom(targetType);
1478     }
1479
1480     private Class<?> findAdapterBoundType(XmlAdapter<?,?> adapter)
1481     {
1482         TypeFactory tf = getTypeFactory();
1483         JavaType adapterType = tf.constructType(adapter.getClass());
1484         JavaType[] params = tf.findTypeParameters(adapterType, XmlAdapter.class);
1485         // should not happen, except if our type resolution has a flaw, but:
1486         if (params == null || params.length < 2) {
1487             return Object.class;
1488         }
1489         return params[1].getRawClass();
1490     }
1491
1492     protected XmlAdapter<?,?> _findContentAdapter(Annotated ann,
1493             boolean forSerialization)
1494     {
1495         Class<?> rawType = forSerialization ?
1496             _rawSerializationType(ann) : _rawDeserializationType(ann);
1497         
1498         // and let's assume this only applies as member annotation:
1499         if (isContainerType(rawType) && (ann instanceof AnnotatedMember)) {
1500             AnnotatedMember member = (AnnotatedMember) ann;
1501             JavaType fullType = forSerialization ?
1502                 _fullSerializationType(member) : _fullDeserializationType(member);
1503             Class<?> contentType = fullType.getContentType().getRawClass();
1504             XmlAdapter<Object,Object> adapter = findAdapter(member, forSerialization, contentType);
1505             if (adapter != null && adapterTypeMatches(adapter, contentType)) {
1506                 return adapter;
1507             }
1508         }
1509         return null;
1510     }
1511     
1512     protected String _propertyNameToString(PropertyName n)
1513     {
1514         return (n == null) ? null : n.getSimpleName();
1515     }
1516
1517     protected Class<?> _rawDeserializationType(Annotated a)
1518     {
1519         if (a instanceof AnnotatedMethod) {
1520             AnnotatedMethod am = (AnnotatedMethod) a;
1521             // 27-Nov-2012, tatu: Bit nasty, as we are assuming
1522             //    things about method signatures here... but has to do
1523             if (am.getParameterCount() == 1) {
1524                 return am.getRawParameterType(0);
1525             }
1526         }
1527         return a.getRawType();
1528     }
1529
1530     protected JavaType _fullDeserializationType(AnnotatedMember am)
1531     {
1532         if (am instanceof AnnotatedMethod) {
1533             AnnotatedMethod method = (AnnotatedMethod) am;
1534             // 27-Nov-2012, tatu: Bit nasty, as we are assuming
1535             //    things about method signatures here... but has to do
1536             if (method.getParameterCount() == 1) {
1537                 return ((AnnotatedMethod) am).getParameterType(0);
1538             }
1539         }
1540         return am.getType();
1541     }
1542
1543     protected Class<?> _rawSerializationType(Annotated a)
1544     {
1545         // 27-Nov-2012, tatu: No work-arounds needed yet...
1546         return a.getRawType();
1547     }
1548
1549     protected JavaType _fullSerializationType(AnnotatedMember am) {
1550         return am.getType();
1551     }
1552
1553     protected Converter<Object,Object> _converter(XmlAdapter<?,?> adapter, boolean forSerialization)
1554     {
1555         TypeFactory tf = getTypeFactory();
1556         JavaType adapterType = tf.constructType(adapter.getClass());
1557         JavaType[] pt = tf.findTypeParameters(adapterType, XmlAdapter.class);
1558         // Order of type parameters for Converter is reverse between serializer, deserializer,
1559         // whereas JAXB just uses single ordering
1560         if (forSerialization) {
1561             return new AdapterConverter(adapter, pt[1], pt[0], forSerialization);
1562         }
1563         return new AdapterConverter(adapter, pt[0], pt[1], forSerialization);
1564     }
1565
1566     protected Class<?> _getTypeFromXmlElement(Annotated a) {
1567         XmlElement annotation = findAnnotation(XmlElement.class, a, falsefalsefalse);
1568         if (annotation != null) {
1569             // Further, JAXB has peculiar notion of declaring intermediate (and, for the
1570             // most part, useless) type... So basically we betterjust ignore type if there
1571             // is adapter annotation (we could check to see if intermediate type is compatible,
1572             // but let's not yet bother)
1573             if (a.getAnnotation(XmlJavaTypeAdapter.class) != null) {
1574                 return null;
1575             }
1576             Class<?> type = annotation.type();
1577             if (type != XmlElement.DEFAULT.class) {
1578                 return type;
1579             }
1580         }
1581         return null;
1582     }
1583 }
1584