1 package com.fasterxml.jackson.dataformat.xml;
2
3 import java.lang.annotation.Annotation;
4
5 import com.fasterxml.jackson.databind.PropertyName;
6 import com.fasterxml.jackson.databind.introspect.*;
7 import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
8 import com.fasterxml.jackson.dataformat.xml.annotation.*;
9
10 /**
11  * Extension of {@link JacksonAnnotationIntrospector} that is needed to support
12  * additional xml-specific annotation that Jackson provides. Note, however, that
13  * there is no JAXB annotation support here; that is provided with
14  * separate introspector (see
15  * {@link com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector}).
16  */

17 public class JacksonXmlAnnotationIntrospector
18     extends JacksonAnnotationIntrospector
19     implements XmlAnnotationIntrospector
20 {
21     private static final long serialVersionUID = 1L;
22
23     // @since 2.11.1
24     @SuppressWarnings("unchecked")
25     private final static Class<? extends Annotation>[] ANNOTATIONS_TO_INFER_XML_PROP =
26             (Class<? extends Annotation>[]) new Class<?>[] {
27         JacksonXmlText.class, JacksonXmlElementWrapper.class
28     };
29
30     /**
31      * For backwards compatibility with 2.0, the default behavior is
32      * to assume use of List wrapper if no annotations are used.
33      */

34     public final static boolean DEFAULT_USE_WRAPPER = true;
35
36     // non-final from 2.7 on, to allow mapper to change
37     protected boolean _cfgDefaultUseWrapper;
38     
39     public JacksonXmlAnnotationIntrospector() {
40         this(DEFAULT_USE_WRAPPER);
41     }
42
43     public JacksonXmlAnnotationIntrospector(boolean defaultUseWrapper) {
44         _cfgDefaultUseWrapper = defaultUseWrapper;
45     }
46
47     /*
48     /**********************************************************************
49     /* Overrides of JacksonAnnotationIntrospector impls
50     /**********************************************************************
51      */

52
53     @Override
54     public PropertyName findWrapperName(Annotated ann)
55     {
56         JacksonXmlElementWrapper w = ann.getAnnotation(JacksonXmlElementWrapper.class);
57         if (w != null) {
58             // Special case: wrapping explicitly blocked?
59             if (!w.useWrapping()) {
60                 return PropertyName.NO_NAME;
61             }
62             // also: need to ensure we use marker:
63             String localName = w.localName();
64             if (localName == null || localName.length() == 0) {
65                 return PropertyName.USE_DEFAULT;
66             }
67             return PropertyName.construct(w.localName(), w.namespace());
68         }
69         /* 09-Sep-2012, tatu: In absence of configurating we need to use our
70          *   default settings...
71          */

72         if (_cfgDefaultUseWrapper) {
73             return PropertyName.USE_DEFAULT;
74         }
75         return null;
76     }
77     
78     @Override
79     public PropertyName findRootName(AnnotatedClass ac)
80     {
81         JacksonXmlRootElement root = ac.getAnnotation(JacksonXmlRootElement.class);
82         if (root != null) {
83             String local = root.localName();
84             String ns = root.namespace();
85             
86             if (local.length() == 0 && ns.length() == 0) {
87                 return PropertyName.USE_DEFAULT;
88             }
89             return new PropertyName(local, ns);
90         }
91         return super.findRootName(ac);
92     }
93     
94     /*
95     /**********************************************************************
96     /* XmlAnnotationIntrospector, findXxx
97     /**********************************************************************
98      */

99
100     @Override
101     public String findNamespace(Annotated ann)
102     {
103         JacksonXmlProperty prop = ann.getAnnotation(JacksonXmlProperty.class);
104         if (prop != null) {
105             return prop.namespace();
106         }
107         return null;
108     }
109
110     /*
111     /**********************************************************************
112     /* XmlAnnotationIntrospector, isXxx methods
113     /**********************************************************************
114      */

115     
116     @Override
117     public Boolean isOutputAsAttribute(Annotated ann)
118     {
119         JacksonXmlProperty prop = ann.getAnnotation(JacksonXmlProperty.class);
120         if (prop != null) {
121             return prop.isAttribute() ? Boolean.TRUE : Boolean.FALSE;
122         }
123         return null;
124     }
125     
126     @Override
127     public Boolean isOutputAsText(Annotated ann)
128     {
129         JacksonXmlText prop = ann.getAnnotation(JacksonXmlText.class);
130         if (prop != null) {
131             return prop.value() ? Boolean.TRUE : Boolean.FALSE;
132         }
133         return null;
134     }
135
136     @Override
137     public Boolean isOutputAsCData(Annotated ann) {
138         JacksonXmlCData prop = ann.getAnnotation(JacksonXmlCData.class);
139         if (prop != null) {
140             return prop.value() ? Boolean.TRUE : Boolean.FALSE;
141         }
142         return null;
143     }
144
145     @Override
146     public void setDefaultUseWrapper(boolean b) {
147         _cfgDefaultUseWrapper = b;
148     }
149
150     /*
151     /**********************************************************************
152     /* Overrides for name, property detection
153     /**********************************************************************
154      */

155
156     @Override
157     public PropertyName findNameForSerialization(Annotated a)
158     {
159         PropertyName name = _findXmlName(a);
160         if (name == null) {
161             name = super.findNameForSerialization(a);
162             if (name == null) {
163                 if (_hasOneOf(a, ANNOTATIONS_TO_INFER_XML_PROP)) {
164                     return PropertyName.USE_DEFAULT;
165                 }
166             }
167         }
168         return name;
169     }
170
171     @Override
172     public PropertyName findNameForDeserialization(Annotated a)
173     {
174         PropertyName name = _findXmlName(a);
175         if (name == null) {
176             name = super.findNameForDeserialization(a);
177             if (name == null) {
178                 if (_hasOneOf(a, ANNOTATIONS_TO_INFER_XML_PROP)) {
179                     return PropertyName.USE_DEFAULT;
180                 }
181             }
182         }
183         return name;
184     }
185     
186     /*
187     /**********************************************************************
188     /* Overrides for non-public helper methods
189     /**********************************************************************
190      */

191
192     /**
193      * We will override this method so that we can return instance
194      * that cleans up type id property name to be a valid xml name.
195      */

196     @Override
197     protected StdTypeResolverBuilder _constructStdTypeResolverBuilder() {
198         return new XmlTypeResolverBuilder();
199     }
200
201     /*
202     /**********************************************************************
203     /* Internal methods
204     /**********************************************************************
205      */

206
207     protected PropertyName _findXmlName(Annotated a)
208     {
209         JacksonXmlProperty pann = a.getAnnotation(JacksonXmlProperty.class);
210         if (pann != null) {
211             return PropertyName.construct(pann.localName(), pann.namespace());
212         }
213         return null;
214     }
215 }
216