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, false, false, false);
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, false, false, false);
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, false, false, false);
258 if (attr != null) {
259 return Boolean.TRUE;
260 }
261 XmlElement elem = findAnnotation(XmlElement.class, ann, false, false, false);
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, false, false, false);
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, false, false, false);
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, true, true, true);
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, false, false, false);
621 XmlElementRefs elemRefs = findAnnotation(XmlElementRefs.class, am, false, false, false);
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, false, false, false);
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, false, false, false);
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, false, false, false);
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, true, true, true);
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, true, true, true);
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 class, package, super-class, since annotation can
1053 // only be attached to fields and methods
1054 XmlElement annotation = findAnnotation(XmlElement.class, a, false, false, false);
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, true, true, true);
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, true, true, true);
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 class, package
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, true, false, true);
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 false, for 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, true, false, false);
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, true, false, false);
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, false, false, false);
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