001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.xml;
014
015import java.util.*;
016import java.util.concurrent.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.annotation.*;
020import org.apache.juneau.collections.*;
021import org.apache.juneau.json.*;
022import org.apache.juneau.serializer.*;
023import org.apache.juneau.xmlschema.*;
024
025/**
026 * Serializes POJO models to XML.
027 *
028 * <h5 class='topic'>Media types</h5>
029 *
030 * Handles <c>Accept</c> types:  <bc>text/xml</bc>
031 * <p>
032 * Produces <c>Content-Type</c> types:  <bc>text/xml</bc>
033 *
034 * <h5 class='topic'>Description</h5>
035 *
036 * See the {@link JsonSerializer} class for details on how Java models map to JSON.
037 *
038 * <p>
039 * For example, the following JSON...
040 * <p class='bcode w800'>
041 *    {
042 *       name:<js>'John Smith'</js>,
043 *       address: {
044 *          streetAddress: <js>'21 2nd Street'</js>,
045 *          city: <js>'New York'</js>,
046 *          state: <js>'NY'</js>,
047 *          postalCode: <js>10021</js>
048 *       },
049 *       phoneNumbers: [
050 *          <js>'212 555-1111'</js>,
051 *          <js>'212 555-2222'</js>
052 *       ],
053 *       additionalInfo: <jk>null</jk>,
054 *       remote: <jk>false</jk>,
055 *       height: <js>62.4</js>,
056 *       <js>'fico score'</js>:  <js>' &gt; 640'</js>
057 *    }
058 * <p>
059 *    ...maps to the following XML using the default serializer...
060 * <p class='bcode w800'>
061 *    <xt>&lt;object&gt;</xt>
062 *       <xt>&lt;name&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
063 *       <xt>&lt;address&gt;</xt>
064 *          <xt>&lt;streetAddress&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
065 *          <xt>&lt;city&gt;</xt>New York<xt>&lt;/city&gt;</xt>
066 *          <xt>&lt;state&gt;</xt>NY<xt>&lt;/state&gt;</xt>
067 *          <xt>&lt;postalCode&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
068 *       <xt>&lt;/address&gt;</xt>
069 *       <xt>&lt;phoneNumbers&gt;</xt>
070 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
071 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
072 *       <xt>&lt;/phoneNumbers&gt;</xt>
073 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
074 *       <xt>&lt;remote&gt;</xt>false<xt>&lt;/remote&gt;</xt>
075 *       <xt>&lt;height&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
076 *       <xt>&lt;fico_x0020_score&gt;</xt> &amp;gt; 640<xt>&lt;/fico_x0020_score&gt;</xt>
077 *    <xt>&lt;/object&gt;</xt>
078 *
079 * <p>
080 * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types...
081 * <p class='bcode w800'>
082 *    <xt>&lt;object&gt;</xt>
083 *       <xt>&lt;name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
084 *       <xt>&lt;address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>&gt;</xt>
085 *          <xt>&lt;streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
086 *          <xt>&lt;city</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>New York<xt>&lt;/city&gt;</xt>
087 *          <xt>&lt;state</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>NY<xt>&lt;/state&gt;</xt>
088 *          <xt>&lt;postalCode</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
089 *       <xt>&lt;/address&gt;</xt>
090 *       <xt>&lt;phoneNumbers</xt> <xa>_type</xa>=<xs>'array'</xs><xt>&gt;</xt>
091 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
092 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
093 *       <xt>&lt;/phoneNumbers&gt;</xt>
094 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
095 *       <xt>&lt;remote</xt> <xa>_type</xa>=<xs>'boolean'</xs><xt>&gt;</xt>false<xt>&lt;/remote&gt;</xt>
096 *       <xt>&lt;height</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
097 *       <xt>&lt;fico_x0020_score</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt> &amp;gt; 640<xt>&lt;/fico_x0020_score&gt;</xt>
098 *    <xt>&lt;/object&gt;</xt>
099 * </p>
100 *
101 * <p>
102 * This serializer provides several serialization options.
103 * Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient.
104 * However, custom serializers can be constructed to fine-tune behavior.
105 *
106 * <p>
107 * If an attribute name contains any non-valid XML element characters, they will be escaped using standard
108 * {@code _x####_} notation.
109 *
110 * <h5 class='topic'>Behavior-specific subclasses</h5>
111 *
112 * The following direct subclasses are provided for convenience:
113 * <ul>
114 *    <li>{@link Sq} - Default serializer, single quotes.
115 *    <li>{@link SqReadable} - Default serializer, single quotes, whitespace added.
116 * </ul>
117 */
118@ConfigurableContext
119public class XmlSerializer extends WriterSerializer implements XmlMetaProvider, XmlCommon {
120
121   //-------------------------------------------------------------------------------------------------------------------
122   // Configurable properties
123   //-------------------------------------------------------------------------------------------------------------------
124
125   static final String PREFIX = "XmlSerializer";
126
127   /**
128    * Configuration property:  Add <js>"_type"</js> properties when needed.
129    *
130    * <h5 class='section'>Property:</h5>
131    * <ul class='spaced-list'>
132    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_addBeanTypes XML_addBeanTypes}
133    *    <li><b>Name:</b>  <js>"XmlSerializer.addBeanTypes.b"</js>
134    *    <li><b>Data type:</b>  <jk>boolean</jk>
135    *    <li><b>System property:</b>  <c>XmlSerializer.addBeanTypes</c>
136    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_ADDBEANTYPES</c>
137    *    <li><b>Default:</b>  <jk>false</jk>
138    *    <li><b>Session property:</b>  <jk>false</jk>
139    *    <li><b>Annotations:</b>
140    *       <ul>
141    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#addBeanTypes()}
142    *       </ul>
143    *    <li><b>Methods:</b>
144    *       <ul>
145    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#addBeanTypes()}
146    *       </ul>
147    * </ul>
148    *
149    * <h5 class='section'>Description:</h5>
150    * <p>
151    * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
152    * through reflection.
153    *
154    * <p>
155    * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is
156    * provided to customize the behavior of specific serializers in a {@link SerializerGroup}.
157    */
158   public static final String XML_addBeanTypes = PREFIX + ".addBeanTypes.b";
159
160   /**
161    * Configuration property:  Add namespace URLs to the root element.
162    *
163    * <h5 class='section'>Property:</h5>
164    * <ul class='spaced-list'>
165    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_addNamespaceUrisToRoot XML_addNamespaceUrisToRoot}
166    *    <li><b>Name:</b>  <js>"XmlSerializer.addNamespaceUrisToRoot.b"</js>
167    *    <li><b>Data type:</b>  <jk>boolean</jk>
168    *    <li><b>System property:</b>  <c>XmlSerializer.addNamespaceUrisToRoot</c>
169    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_ADDNAMESPACEURISTOROOT</c>
170    *    <li><b>Default:</b>  <jk>false</jk>
171    *    <li><b>Session property:</b>  <jk>false</jk>
172    *    <li><b>Annotations:</b>
173    *       <ul>
174    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#addNamespaceUrisToRoot()}
175    *       </ul>
176    *    <li><b>Methods:</b>
177    *       <ul>
178    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#addNamespaceUrisToRoot()}
179    *       </ul>
180    * </ul>
181    *
182    * <h5 class='section'>Description:</h5>
183    * <p>
184    * Use this setting to add {@code xmlns:x} attributes to the root element for the default and all mapped namespaces.
185    *
186    * <p>
187    * This setting is ignored if {@link #XML_enableNamespaces} is not enabled.
188    *
189    * <ul class='seealso'>
190    *    <li class='link'>{@doc XmlNamespaces}
191    * </ul>
192    */
193   public static final String XML_addNamespaceUrisToRoot = PREFIX + ".addNamespaceUrisToRoot.b";
194
195   /**
196    * Configuration property:  Auto-detect namespace usage.
197    *
198    * <h5 class='section'>Property:</h5>
199    * <ul class='spaced-list'>
200    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_autoDetectNamespaces XML_autoDetectNamespaces}
201    *    <li><b>Name:</b>  <js>"XmlSerializer.autoDetectNamespaces.b"</js>
202    *    <li><b>Data type:</b>  <jk>boolean</jk>
203    *    <li><b>System property:</b>  <c>XmlSerializer.autoDetectNamespaces</c>
204    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_AUTODETECTNAMESPACES</c>
205    *    <li><b>Default:</b>  <jk>true</jk>
206    *    <li><b>Session property:</b>  <jk>false</jk>
207    *    <li><b>Annotations:</b>
208    *       <ul>
209    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#autoDetectNamespaces()}
210    *       </ul>
211    *    <li><b>Methods:</b>
212    *       <ul>
213    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#dontAutoDetectNamespaces()}
214    *       </ul>
215    * </ul>
216    *
217    * <h5 class='section'>Description:</h5>
218    * <p>
219    * Detect namespace usage before serialization.
220    *
221    * <p>
222    * Used in conjunction with {@link #XML_addNamespaceUrisToRoot} to reduce the list of namespace URLs appended to the
223    * root element to only those that will be used in the resulting document.
224    *
225    * <p>
226    * If enabled, then the data structure will first be crawled looking for namespaces that will be encountered before
227    * the root element is serialized.
228    *
229    * <p>
230    * This setting is ignored if {@link #XML_enableNamespaces} is not enabled.
231    *
232    * <ul class='notes'>
233    *    <li>
234    *       Auto-detection of namespaces can be costly performance-wise.
235    *       <br>In high-performance environments, it's recommended that namespace detection be
236    *       disabled, and that namespaces be manually defined through the {@link #XML_namespaces} property.
237    * </ul>
238    *
239    * <ul class='seealso'>
240    *    <li class='link'>{@doc XmlNamespaces}
241    * </ul>
242    */
243   public static final String XML_autoDetectNamespaces = PREFIX + ".autoDetectNamespaces.b";
244
245   /**
246    * Configuration property:  Default namespace.
247    *
248    * <h5 class='section'>Property:</h5>
249    * <ul class='spaced-list'>
250    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_defaultNamespace XML_defaultNamespace}
251    *    <li><b>Name:</b>  <js>"XmlSerializer.defaultNamespace.s"</js>
252    *    <li><b>Data type:</b>  {@link org.apache.juneau.xml.Namespace}
253    *    <li><b>System property:</b>  <c>XmlSerializer.defaultNamespace</c>
254    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_DEFAULTNAMESPACE</c>
255    *    <li><b>Default:</b>  <js>"juneau: http://www.apache.org/2013/Juneau"</js>
256    *    <li><b>Session property:</b>  <jk>false</jk>
257    *    <li><b>Annotations:</b>
258    *       <ul>
259    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#defaultNamespace()}
260    *       </ul>
261    *    <li><b>Methods:</b>
262    *       <ul>
263    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#defaultNamespace(String)}
264    *       </ul>
265    * </ul>
266    *
267    * <h5 class='section'>Description:</h5>
268    * <p>
269    * Specifies the default namespace URI for this document.
270    *
271    * <ul class='seealso'>
272    *    <li class='link'>{@doc XmlNamespaces}
273    * </ul>
274    */
275   public static final String XML_defaultNamespace = PREFIX + ".defaultNamespace.s";
276
277   /**
278    * Configuration property:  Enable support for XML namespaces.
279    *
280    * <h5 class='section'>Property:</h5>
281    * <ul class='spaced-list'>
282    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_enableNamespaces XML_enableNamespaces}
283    *    <li><b>Name:</b>  <js>"XmlSerializer.enableNamespaces.b"</js>
284    *    <li><b>Data type:</b>  <jk>boolean</jk>
285    *    <li><b>System property:</b>  <c>XmlSerializer.enableNamespaces</c>
286    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_ENABLENAMESPACES</c>
287    *    <li><b>Default:</b>  <jk>false</jk>
288    *    <li><b>Session property:</b>  <jk>false</jk>
289    *    <li><b>Annotations:</b>
290    *       <ul>
291    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#enableNamespaces()}
292    *       </ul>
293    *    <li><b>Methods:</b>
294    *       <ul>
295    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#enableNamespaces()}
296    *       </ul>
297    * </ul>
298    *
299    * <h5 class='section'>Description:</h5>
300    * <p>
301    * If not enabled, XML output will not contain any namespaces regardless of any other settings.
302    *
303    * <ul class='seealso'>
304    *    <li class='link'>{@doc XmlNamespaces}
305    * </ul>
306    */
307   public static final String XML_enableNamespaces = PREFIX + ".enableNamespaces.b";
308
309   /**
310    * Configuration property:  Default namespaces.
311    *
312    * <h5 class='section'>Property:</h5>
313    * <ul class='spaced-list'>
314    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_namespaces XML_namespaces}
315    *    <li><b>Name:</b>  <js>"XmlSerializer.namespaces.ls"</js>
316    *    <li><b>Data type:</b>  <c>Set&lt;{@link org.apache.juneau.xml.Namespace}&gt;</c>
317    *    <li><b>System property:</b>  <c>XmlSerializer.namespaces</c>
318    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_NAMESPACES</c>
319    *    <li><b>Default:</b>  empty set
320    *    <li><b>Session property:</b>  <jk>false</jk>
321    *    <li><b>Annotations:</b>
322    *       <ul>
323    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#defaultNamespace()}
324    *       </ul>
325    *    <li><b>Methods:</b>
326    *       <ul>
327    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#defaultNamespace(String)}
328    *       </ul>
329    * </ul>
330    *
331    * <h5 class='section'>Description:</h5>
332    * <p>
333    * The default list of namespaces associated with this serializer.
334    *
335    * <ul class='seealso'>
336    *    <li class='link'>{@doc XmlNamespaces}
337    * </ul>
338    */
339   public static final String XML_namespaces = PREFIX + ".namespaces.ls";
340
341   /**
342    * Configuration property:  XMLSchema namespace.
343    *
344    * <h5 class='section'>Property:</h5>
345    * <ul class='spaced-list'>
346    *    <li><b>ID:</b>  {@link org.apache.juneau.xml.XmlSerializer#XML_xsNamespace XML_xsNamespace}
347    *    <li><b>Name:</b>  <js>"XmlSerializer.xsNamespace.s"</js>
348    *    <li><b>Data type:</b>  <c>String</c> ({@link org.apache.juneau.xml.Namespace})
349    *    <li><b>System property:</b>  <c>XmlSerializer.xsNamespace</c>
350    *    <li><b>Environment variable:</b>  <c>XMLSERIALIZER_XSNAMESPACE</c>
351    *    <li><b>Default:</b>  <js>"xs: http://www.w3.org/2001/XMLSchema"</js>
352    *    <li><b>Session property:</b>  <jk>false</jk>
353    *    <li><b>Annotations:</b>
354    *       <ul>
355    *          <li class='ja'>{@link org.apache.juneau.xml.annotation.XmlConfig#xsNamespace()}
356    *       </ul>
357    *    <li><b>Methods:</b>
358    *       <ul>
359    *          <li class='jm'>{@link org.apache.juneau.xml.XmlSerializerBuilder#xsNamespace(Namespace)}
360    *       </ul>
361    * </ul>
362    *
363    * <h5 class='section'>Description:</h5>
364    * <p>
365    * Specifies the namespace for the <c>XMLSchema</c> namespace, used by the schema generated by the
366    * {@link XmlSchemaSerializer} class.
367    *
368    * <ul class='seealso'>
369    *    <li class='link'>{@doc XmlNamespaces}
370    * </ul>
371    */
372   @Deprecated
373   public static final String XML_xsNamespace = PREFIX + ".xsNamespace.s";
374
375
376   //-------------------------------------------------------------------------------------------------------------------
377   // Predefined instances
378   //-------------------------------------------------------------------------------------------------------------------
379
380   /** Default serializer without namespaces. */
381   public static final XmlSerializer DEFAULT = new XmlSerializer(PropertyStore.DEFAULT);
382
383   /** Default serializer without namespaces, with single quotes. */
384   public static final XmlSerializer DEFAULT_SQ = new Sq(PropertyStore.DEFAULT);
385
386   /** Default serializer without namespaces, with single quotes, whitespace added. */
387   public static final XmlSerializer DEFAULT_SQ_READABLE = new SqReadable(PropertyStore.DEFAULT);
388
389   /** Default serializer, all default settings. */
390   public static final XmlSerializer DEFAULT_NS = new Ns(PropertyStore.DEFAULT);
391
392   /** Default serializer, single quotes. */
393   public static final XmlSerializer DEFAULT_NS_SQ = new NsSq(PropertyStore.DEFAULT);
394
395   /** Default serializer, single quotes, whitespace added. */
396   public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new NsSqReadable(PropertyStore.DEFAULT);
397
398
399   //-------------------------------------------------------------------------------------------------------------------
400   // Predefined subclasses
401   //-------------------------------------------------------------------------------------------------------------------
402
403   /** Default serializer, single quotes. */
404   public static class Sq extends XmlSerializer {
405
406      /**
407       * Constructor.
408       *
409       * @param ps The property store containing all the settings for this object.
410       */
411      public Sq(PropertyStore ps) {
412         super(
413            ps.builder()
414               .setDefault(WSERIALIZER_quoteChar, '\'')
415               .build()
416            );
417      }
418   }
419
420   /** Default serializer, single quotes, whitespace added. */
421   public static class SqReadable extends XmlSerializer {
422
423      /**
424       * Constructor.
425       *
426       * @param ps The property store containing all the settings for this object.
427       */
428      public SqReadable(PropertyStore ps) {
429         super(
430            ps.builder()
431               .setDefault(WSERIALIZER_quoteChar, '\'')
432               .setDefault(WSERIALIZER_useWhitespace, true)
433               .build()
434            );
435      }
436   }
437
438   /** Default serializer without namespaces. */
439   public static class Ns extends XmlSerializer {
440
441      /**
442       * Constructor.
443       *
444       * @param ps The property store containing all the settings for this object.
445       */
446      public Ns(PropertyStore ps) {
447         super(
448            ps.builder()
449               .setDefault(XML_enableNamespaces, true)
450               .build(),
451            "text/xml",
452            "text/xml+simple"
453         );
454      }
455   }
456
457   /** Default serializer without namespaces, single quotes. */
458   public static class NsSq extends XmlSerializer {
459
460      /**
461       * Constructor.
462       *
463       * @param ps The property store containing all the settings for this object.
464       */
465      public NsSq(PropertyStore ps) {
466         super(
467            ps.builder()
468               .setDefault(XML_enableNamespaces, true)
469               .setDefault(WSERIALIZER_quoteChar, '\'')
470               .build()
471            );
472      }
473   }
474
475   /** Default serializer without namespaces, single quotes, with whitespace. */
476   public static class NsSqReadable extends XmlSerializer {
477
478      /**
479       * Constructor.
480       *
481       * @param ps The property store containing all the settings for this object.
482       */
483      public NsSqReadable(PropertyStore ps) {
484         super(
485            ps.builder()
486               .setDefault(XML_enableNamespaces, true)
487               .setDefault(WSERIALIZER_quoteChar, '\'')
488               .setDefault(WSERIALIZER_useWhitespace, true)
489               .build()
490            );
491      }
492   }
493
494   @SuppressWarnings("javadoc")
495   protected static final Namespace
496      DEFAULT_JUNEAU_NAMESPACE = Namespace.create("juneau", "http://www.apache.org/2013/Juneau"),
497      DEFAULT_XS_NAMESPACE = Namespace.create("xs", "http://www.w3.org/2001/XMLSchema");
498
499   //-------------------------------------------------------------------------------------------------------------------
500   // Instance
501   //-------------------------------------------------------------------------------------------------------------------
502
503   private final boolean
504      autoDetectNamespaces,
505      enableNamespaces,
506      addNamespaceUrlsToRoot,
507      addBeanTypes;
508   private final Namespace defaultNamespace;
509   private final Namespace
510      xsNamespace;
511   private final Namespace[] namespaces;
512   private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
513   private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
514   private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
515
516   @Deprecated
517   private volatile XmlSchemaSerializer schemaSerializer;
518
519   /**
520    * Constructor.
521    *
522    * @param ps
523    *    The property store containing all the settings for this object.
524    */
525   public XmlSerializer(PropertyStore ps) {
526      this(ps, "text/xml", (String)null);
527   }
528
529   /**
530    * Constructor.
531    *
532    * @param ps
533    *    The property store containing all the settings for this object.
534    * @param produces
535    *    The media type that this serializer produces.
536    * @param accept
537    *    The accept media types that the serializer can handle.
538    *    <p>
539    *    Can contain meta-characters per the <c>media-type</c> specification of {@doc ExtRFC2616.section14.1}
540    *    <p>
541    *    If empty, then assumes the only media type supported is <c>produces</c>.
542    *    <p>
543    *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
544    *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
545    *    <p class='bcode w800'>
546    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>);
547    *    </p>
548    *    <br>...or...
549    *    <p class='bcode w800'>
550    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*&#8203;/json"</js>);
551    *    </p>
552    * <p>
553    * The accept value can also contain q-values.
554    */
555   public XmlSerializer(PropertyStore ps, String produces, String accept) {
556      super(ps, produces, accept);
557      autoDetectNamespaces = getBooleanProperty(XML_autoDetectNamespaces, true);
558      enableNamespaces = getBooleanProperty(XML_enableNamespaces, false);
559      addNamespaceUrlsToRoot = getBooleanProperty(XML_addNamespaceUrisToRoot, false);
560      defaultNamespace = getInstanceProperty(XML_defaultNamespace, Namespace.class, DEFAULT_JUNEAU_NAMESPACE);
561      addBeanTypes = getBooleanProperty(XML_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false));
562      xsNamespace = getInstanceProperty(XML_xsNamespace, Namespace.class, DEFAULT_XS_NAMESPACE);
563      namespaces = getInstanceArrayProperty(XML_namespaces, Namespace.class, new Namespace[0]);
564   }
565
566   @Override /* Context */
567   public XmlSerializerBuilder builder() {
568      return new XmlSerializerBuilder(getPropertyStore());
569   }
570
571   /**
572    * Instantiates a new clean-slate {@link XmlSerializerBuilder} object.
573    *
574    * <p>
575    * This is equivalent to simply calling <code><jk>new</jk> XmlSerializerBuilder()</code>.
576    *
577    * <p>
578    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
579    * the settings of the object called on.
580    *
581    * @return A new {@link XmlSerializerBuilder} object.
582    */
583   public static XmlSerializerBuilder create() {
584      return new XmlSerializerBuilder();
585   }
586
587   /**
588    * Returns the schema serializer based on the settings of this serializer.
589    * @return The schema serializer.
590    */
591   @Deprecated
592   public XmlSerializer getSchemaSerializer() {
593      if (schemaSerializer == null)
594         schemaSerializer = builder().build(XmlSchemaSerializer.class);
595      return schemaSerializer;
596   }
597
598   @Override /* Serializer */
599   public XmlSerializerSession createSession() {
600      return createSession(createDefaultSessionArgs());
601   }
602
603   @Override /* Serializer */
604   public XmlSerializerSession createSession(SerializerSessionArgs args) {
605      return new XmlSerializerSession(this, args);
606   }
607
608   //-----------------------------------------------------------------------------------------------------------------
609   // Extended metadata
610   //-----------------------------------------------------------------------------------------------------------------
611
612   @Override /* XmlMetaProvider */
613   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
614      XmlClassMeta m = xmlClassMetas.get(cm);
615      if (m == null) {
616         m = new XmlClassMeta(cm, this);
617         xmlClassMetas.put(cm, m);
618      }
619      return m;
620   }
621
622   @Override /* XmlMetaProvider */
623   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
624      XmlBeanMeta m = xmlBeanMetas.get(bm);
625      if (m == null) {
626         m = new XmlBeanMeta(bm, this);
627         xmlBeanMetas.put(bm, m);
628      }
629      return m;
630   }
631
632   @Override /* XmlMetaProvider */
633   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
634      XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
635      if (m == null) {
636         m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
637         xmlBeanPropertyMetas.put(bpm, m);
638      }
639      return m;
640   }
641
642   //-----------------------------------------------------------------------------------------------------------------
643   // Properties
644   //-----------------------------------------------------------------------------------------------------------------
645
646   /**
647    * Add <js>"_type"</js> properties when needed.
648    *
649    * @see #XML_addBeanTypes
650    * @return
651    *    <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred
652    *    through reflection.
653    */
654   @Override
655   protected boolean isAddBeanTypes() {
656      return addBeanTypes;
657   }
658
659   /**
660    * Add namespace URLs to the root element.
661    *
662    * @see #XML_addNamespaceUrisToRoot
663    * @return
664    *    <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces.
665    */
666   protected final boolean isAddNamespaceUrlsToRoot() {
667      return addNamespaceUrlsToRoot;
668   }
669
670   /**
671    * Auto-detect namespace usage.
672    *
673    * @see #XML_autoDetectNamespaces
674    * @return
675    *    <jk>true</jk> if namespace usage is detected before serialization.
676    */
677   protected final boolean isAutoDetectNamespaces() {
678      return autoDetectNamespaces;
679   }
680
681   /**
682    * Default namespace.
683    *
684    * @see #XML_defaultNamespace
685    * @return
686    *    The default namespace URI for this document.
687    */
688   protected final Namespace getDefaultNamespace() {
689      return defaultNamespace;
690   }
691
692   /**
693    * Enable support for XML namespaces.
694    *
695    * @see #XML_enableNamespaces
696    * @return
697    *    <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings.
698    */
699   protected final boolean isEnableNamespaces() {
700      return enableNamespaces;
701   }
702
703   /**
704    * Default namespaces.
705    *
706    * @see #XML_namespaces
707    * @return
708    *    The default list of namespaces associated with this serializer.
709    */
710   protected final Namespace[] getNamespaces() {
711      return namespaces;
712   }
713
714   /**
715    * XMLSchema namespace.
716    *
717    * @see #XML_xsNamespace
718    * @return
719    *    The namespace for the <c>XMLSchema</c> namespace, used by the schema generated by the
720    *    {@link XmlSchemaSerializer} class.
721    */
722   @Deprecated
723   protected final Namespace getXsNamespace() {
724      return xsNamespace;
725   }
726
727   //-----------------------------------------------------------------------------------------------------------------
728   // Other methods
729   //-----------------------------------------------------------------------------------------------------------------
730
731   @Override /* Context */
732   public OMap toMap() {
733      return super.toMap()
734         .a("XmlSerializer", new DefaultFilteringOMap()
735            .a("autoDetectNamespaces", autoDetectNamespaces)
736            .a("enableNamespaces", enableNamespaces)
737            .a("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot)
738            .a("defaultNamespace", defaultNamespace)
739            .a("xsNamespace", xsNamespace)
740            .a("namespaces", namespaces)
741            .a("addBeanTypes", addBeanTypes)
742         );
743   }
744}