1 package com.fasterxml.jackson.dataformat.xml.deser;
2
3 import java.util.*;
4
5 import com.fasterxml.jackson.databind.*;
6 import com.fasterxml.jackson.databind.deser.*;
7 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
8 import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
9 import com.fasterxml.jackson.dataformat.xml.util.AnnotationUtil;
10
11 /**
12  * The main reason for a modifier is to support handling of
13  * 'wrapped' Collection types.
14  */

15 public class XmlBeanDeserializerModifier
16     extends BeanDeserializerModifier
17     implements java.io.Serializable
18 {
19     private static final long serialVersionUID = 1L;
20
21     /**
22      * Virtual name used for text segments.
23      */

24     protected String _cfgNameForTextValue = "";
25
26     public XmlBeanDeserializerModifier(String nameForTextValue)
27     {
28         _cfgNameForTextValue = nameForTextValue;
29     }
30     
31     @Override
32     public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config,
33             BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs)
34     {
35         final AnnotationIntrospector intr = config.getAnnotationIntrospector();
36         int changed = 0;
37         
38         for (int i = 0, propCount = propDefs.size(); i < propCount; ++i) {
39             BeanPropertyDefinition prop = propDefs.get(i);
40             AnnotatedMember acc = prop.getPrimaryMember();
41             // should not be null, but just in case:
42             if (acc == null) {
43                 continue;
44             }
45             /* First: handle "as text"? Such properties
46              * are exposed as values of 'unnamed' fields; so one way to
47              * map them is to rename property to have name ""... (and
48              * hope this does not break other parts...)
49              */

50             Boolean b = AnnotationUtil.findIsTextAnnotation(intr, acc);
51             if (b != null && b.booleanValue()) {
52                 // unwrapped properties will appear as 'unnamed' (empty String)
53                 BeanPropertyDefinition newProp = prop.withSimpleName(_cfgNameForTextValue);
54                 if (newProp != prop) {
55                     propDefs.set(i, newProp);
56                 }
57                 continue;
58             }
59             // second: do we need to handle wrapping (for Lists)?
60             PropertyName wrapperName = prop.getWrapperName();
61             
62             if (wrapperName != null && wrapperName != PropertyName.NO_NAME) {
63                 String localName = wrapperName.getSimpleName();
64                 if ((localName != null && localName.length() > 0)
65                         && !localName.equals(prop.getName())) {
66                     // make copy-on-write as necessary
67                     if (changed == 0) {
68                         propDefs = new ArrayList<BeanPropertyDefinition>(propDefs);
69                     }
70                     ++changed;
71                     propDefs.set(i, prop.withSimpleName(localName));
72                     continue;
73                 }
74                 // otherwise unwrapped; needs handling but later on
75             }
76         }
77         return propDefs;
78     }
79
80     @Override
81     public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
82             BeanDescription beanDesc, JsonDeserializer<?> deser0)
83     {
84         if (!(deser0 instanceof BeanDeserializerBase)) {
85             return deser0;
86         }
87         /* 17-Aug-2013, tatu: One important special case first: if we have one "XML Text"
88          * property, it may be exposed as VALUE_STRING token (depending on whether any attribute
89          * values are exposed): and to deserialize from that, we need special handling unless POJO
90          * has appropriate single-string creator method.
91          */

92         BeanDeserializerBase deser = (BeanDeserializerBase) deser0;
93
94         // Heuristics are bit tricky; but for now let's assume that if POJO
95         // can already work with VALUE_STRING, it's ok and doesn't need extra support
96         ValueInstantiator inst = deser.getValueInstantiator();
97         // 03-Aug-2017, tatu: [dataformat-xml#254] suggests we also should
98         //    allow passing `int`/`Integer`/`long`/`Long` cases, BUT
99         //    unfortunately we can not simple use default handling. Would need
100         //    coercion.
101         if (!inst.canCreateFromString()) {
102             SettableBeanProperty textProp = _findSoleTextProp(config, deser.properties());
103             if (textProp != null) {
104                 return new XmlTextDeserializer(deser, textProp);
105             }
106         }
107         return new WrapperHandlingDeserializer(deser);
108     }
109
110     private SettableBeanProperty _findSoleTextProp(DeserializationConfig config,
111             Iterator<SettableBeanProperty> propIt)
112     {
113         final AnnotationIntrospector ai = config.getAnnotationIntrospector();
114         SettableBeanProperty textProp = null;
115         while (propIt.hasNext()) {
116             SettableBeanProperty prop = propIt.next();
117             AnnotatedMember m = prop.getMember();
118             if (m != null) {
119                 // Ok, let's use a simple check: we should have renamed it earlier so:
120                 PropertyName n = prop.getFullName();
121                 if (_cfgNameForTextValue.equals(n.getSimpleName())) {
122                     // should we verify we only got one?
123                     textProp = prop;
124                     continue;
125                 }
126                 // as-attribute are ok as well
127                 Boolean b = AnnotationUtil.findIsAttributeAnnotation(ai, m);
128                 if (b != null && b.booleanValue()) {
129                     continue;
130                 }
131             }
132             // Otherwise, it's something else; no go
133             return null;
134         }
135         return textProp;
136     }
137 }
138