001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.xml;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022
023import java.lang.annotation.*;
024import java.nio.charset.*;
025import java.util.*;
026import java.util.concurrent.*;
027
028import org.apache.juneau.*;
029import org.apache.juneau.commons.collections.*;
030import org.apache.juneau.commons.function.*;
031import org.apache.juneau.commons.reflect.*;
032import org.apache.juneau.json.*;
033import org.apache.juneau.serializer.*;
034
035/**
036 * Serializes POJO models to XML.
037 *
038 * <h5 class='topic'>Media types</h5>
039 * <p>
040 * Handles <c>Accept</c> types:  <bc>text/xml</bc>
041 * <p>
042 * Produces <c>Content-Type</c> types:  <bc>text/xml</bc>
043 *
044 * <h5 class='topic'>Description</h5>
045 * <p>
046 * See the {@link JsonSerializer} class for details on how Java models map to JSON.
047 *
048 * <p>
049 * For example, the following JSON...
050 * <p class='bjson'>
051 *    {
052 *       name:<js>'John Smith'</js>,
053 *       address: {
054 *          streetAddress: <js>'21 2nd Street'</js>,
055 *          city: <js>'New York'</js>,
056 *          state: <js>'NY'</js>,
057 *          postalCode: <js>10021</js>
058 *       },
059 *       phoneNumbers: [
060 *          <js>'212 555-1111'</js>,
061 *          <js>'212 555-2222'</js>
062 *       ],
063 *       additionalInfo: <jk>null</jk>,
064 *       remote: <jk>false</jk>,
065 *       height: <js>62.4</js>,
066 *       <js>'fico score'</js>:  <js>' &gt; 640'</js>
067 *    }
068 * <p>
069 *    ...maps to the following XML using the default serializer...
070 * <p class='bxml'>
071 *    <xt>&lt;object&gt;</xt>
072 *       <xt>&lt;name&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
073 *       <xt>&lt;address&gt;</xt>
074 *          <xt>&lt;streetAddress&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
075 *          <xt>&lt;city&gt;</xt>New York<xt>&lt;/city&gt;</xt>
076 *          <xt>&lt;state&gt;</xt>NY<xt>&lt;/state&gt;</xt>
077 *          <xt>&lt;postalCode&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
078 *       <xt>&lt;/address&gt;</xt>
079 *       <xt>&lt;phoneNumbers&gt;</xt>
080 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
081 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
082 *       <xt>&lt;/phoneNumbers&gt;</xt>
083 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
084 *       <xt>&lt;remote&gt;</xt>false<xt>&lt;/remote&gt;</xt>
085 *       <xt>&lt;height&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
086 *       <xt>&lt;fico_x0020_score&gt;</xt> &amp;gt; 640<xt>&lt;/fico_x0020_score&gt;</xt>
087 *    <xt>&lt;/object&gt;</xt>
088 *
089 * <p>
090 * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types...
091 * <p class='bxml'>
092 *    <xt>&lt;object&gt;</xt>
093 *       <xt>&lt;name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
094 *       <xt>&lt;address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>&gt;</xt>
095 *          <xt>&lt;streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
096 *          <xt>&lt;city</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>New York<xt>&lt;/city&gt;</xt>
097 *          <xt>&lt;state</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>NY<xt>&lt;/state&gt;</xt>
098 *          <xt>&lt;postalCode</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
099 *       <xt>&lt;/address&gt;</xt>
100 *       <xt>&lt;phoneNumbers</xt> <xa>_type</xa>=<xs>'array'</xs><xt>&gt;</xt>
101 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
102 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
103 *       <xt>&lt;/phoneNumbers&gt;</xt>
104 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
105 *       <xt>&lt;remote</xt> <xa>_type</xa>=<xs>'boolean'</xs><xt>&gt;</xt>false<xt>&lt;/remote&gt;</xt>
106 *       <xt>&lt;height</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
107 *       <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>
108 *    <xt>&lt;/object&gt;</xt>
109 * </p>
110 *
111 * <p>
112 * This serializer provides several serialization options.
113 * Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient.
114 * However, custom serializers can be constructed to fine-tune behavior.
115 *
116 * <p>
117 * If an attribute name contains any non-valid XML element characters, they will be escaped using standard
118 * {@code _x####_} notation.
119 *
120 * <h5 class='topic'>Behavior-specific subclasses</h5>
121 * <p>
122 * The following direct subclasses are provided for convenience:
123 * <ul>
124 *    <li>{@link Sq} - Default serializer, single quotes.
125 *    <li>{@link SqReadable} - Default serializer, single quotes, whitespace added.
126 * </ul>
127 *
128 * <h5 class='section'>Notes:</h5><ul>
129 *    <li class='note'>This class is thread safe and reusable.
130 * </ul>
131 *
132 * <h5 class='section'>See Also:</h5><ul>
133 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlBasics">XML Basics</a>
134
135 * </ul>
136 */
137public class XmlSerializer extends WriterSerializer implements XmlMetaProvider {
138   /**
139    * Builder class.
140    */
141   public static class Builder extends WriterSerializer.Builder {
142
143      private static final Cache<HashKey,XmlSerializer> CACHE = Cache.of(HashKey.class, XmlSerializer.class).build();
144
145      private boolean addBeanTypesXml;
146      private boolean addNamespaceUrisToRoot;
147      private boolean disableAutoDetectNamespaces;
148      private boolean disableJsonTags;
149      private boolean enableNamespaces;
150      private Namespace defaultNamespace;
151      private List<Namespace> namespaces;
152      private String textNodeDelimiter;
153
154      /**
155       * Constructor, default settings.
156       */
157      protected Builder() {
158         produces("text/xml");
159         addBeanTypesXml = env("XmlSerializer.addBeanTypes", false);
160         addNamespaceUrisToRoot = env("XmlSerializer.addNamespaceUrisToRoot", false);
161         disableAutoDetectNamespaces = env("XmlSerializer.disableAutoDetectNamespaces", false);
162         disableJsonTags = env("XmlSerializer.disableJsonTags", false);
163         enableNamespaces = env("XmlSerializer.enableNamespaces", false);
164         defaultNamespace = null;
165         namespaces = null;
166         textNodeDelimiter = env("XmlSerializer.textNodeDelimiter", "");
167      }
168
169      /**
170       * Copy constructor.
171       *
172       * @param copyFrom The builder to copy from.
173       *    <br>Cannot be <jk>null</jk>.
174       */
175      protected Builder(Builder copyFrom) {
176         super(assertArgNotNull("copyFrom", copyFrom));
177         addBeanTypesXml = copyFrom.addBeanTypesXml;
178         addNamespaceUrisToRoot = copyFrom.addNamespaceUrisToRoot;
179         defaultNamespace = copyFrom.defaultNamespace;
180         disableAutoDetectNamespaces = copyFrom.disableAutoDetectNamespaces;
181         disableJsonTags = copyFrom.disableJsonTags;
182         enableNamespaces = copyFrom.enableNamespaces;
183         namespaces = copyFrom.namespaces == null ? null : new ArrayList<>(copyFrom.namespaces);
184         textNodeDelimiter = copyFrom.textNodeDelimiter;
185      }
186
187      /**
188       * Copy constructor.
189       *
190       * @param copyFrom The bean to copy from.
191       *    <br>Cannot be <jk>null</jk>.
192       */
193      protected Builder(XmlSerializer copyFrom) {
194         super(assertArgNotNull("copyFrom", copyFrom));
195         addBeanTypesXml = copyFrom.addBeanTypesXml;
196         addNamespaceUrisToRoot = copyFrom.addNamespaceUrlsToRoot;
197         defaultNamespace = copyFrom.getDefaultNamespace();
198         disableAutoDetectNamespaces = ! copyFrom.autoDetectNamespaces;
199         disableJsonTags = ! copyFrom.addJsonTags;
200         enableNamespaces = copyFrom.enableNamespaces;
201         var ctxNamespaces = copyFrom.getNamespaces();
202         namespaces = ctxNamespaces.isEmpty() ? null : new ArrayList<>(ctxNamespaces);
203         textNodeDelimiter = copyFrom.getTextNodeDelimiter();
204      }
205
206      @Override /* Overridden from Builder */
207      public Builder accept(String value) {
208         super.accept(value);
209         return this;
210      }
211
212      @Override /* Overridden from Builder */
213      public Builder addBeanTypes() {
214         super.addBeanTypes();
215         return this;
216      }
217
218      @Override /* Overridden from Builder */
219      public Builder addBeanTypes(boolean value) {
220         super.addBeanTypes(value);
221         return this;
222      }
223
224      /**
225       * Add <js>"_type"</js> properties when needed.
226       *
227       * <p>
228       * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
229       * through reflection.
230       *
231       * <p>
232       * When present, this value overrides the {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} setting and is
233       * provided to customize the behavior of specific serializers in a {@link SerializerSet}.
234       *
235       * @return This object.
236       */
237      public Builder addBeanTypesXml() {
238         return addBeanTypesXml(true);
239      }
240
241      /**
242       * Same as {@link #addBeanTypesXml()} but allows you to explicitly specify the value.
243       *
244       * @param value The value for this setting.
245       * @return This object.
246       */
247      public Builder addBeanTypesXml(boolean value) {
248         addBeanTypesXml = value;
249         return this;
250      }
251
252      /**
253       * Add namespace URLs to the root element.
254       *
255       * <p>
256       * Use this setting to add {@code xmlns:x} attributes to the root element for the default and all mapped namespaces.
257       *
258       * <p>
259       * This setting is ignored if {@link #enableNamespaces()} is not enabled.
260       *
261       * <h5 class='section'>See Also:</h5><ul>
262       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a>
263       * </ul>
264       *
265       * @return This object.
266       */
267      public Builder addNamespaceUrisToRoot() {
268         return addNamespaceUrisToRoot(true);
269      }
270
271      /**
272       * Same as {@link #addNamespaceUrisToRoot()} but allows you to explicitly specify the value.
273       *
274       * @param value The value for this setting.
275       * @return This object.
276       */
277      public Builder addNamespaceUrisToRoot(boolean value) {
278         addNamespaceUrisToRoot = value;
279         return this;
280      }
281
282      @Override /* Overridden from Builder */
283      public Builder addRootType() {
284         super.addRootType();
285         return this;
286      }
287
288      @Override /* Overridden from Builder */
289      public Builder addRootType(boolean value) {
290         super.addRootType(value);
291         return this;
292      }
293
294      @Override /* Overridden from Builder */
295      public Builder annotations(Annotation...values) {
296         super.annotations(values);
297         return this;
298      }
299
300      @Override /* Overridden from Builder */
301      public Builder apply(AnnotationWorkList work) {
302         super.apply(work);
303         return this;
304      }
305
306      @Override /* Overridden from Builder */
307      public Builder applyAnnotations(Class<?>...from) {
308         super.applyAnnotations(from);
309         return this;
310      }
311
312      @Override /* Overridden from Builder */
313      public Builder applyAnnotations(Object...from) {
314         super.applyAnnotations(from);
315         return this;
316      }
317
318      @Override /* Overridden from Builder */
319      public Builder beanClassVisibility(Visibility value) {
320         super.beanClassVisibility(value);
321         return this;
322      }
323
324      @Override /* Overridden from Builder */
325      public Builder beanConstructorVisibility(Visibility value) {
326         super.beanConstructorVisibility(value);
327         return this;
328      }
329
330      @Override /* Overridden from Builder */
331      public Builder beanContext(BeanContext value) {
332         super.beanContext(value);
333         return this;
334      }
335
336      @Override /* Overridden from Builder */
337      public Builder beanContext(BeanContext.Builder value) {
338         super.beanContext(value);
339         return this;
340      }
341
342      @Override /* Overridden from Builder */
343      public Builder beanDictionary(java.lang.Class<?>...values) {
344         super.beanDictionary(values);
345         return this;
346      }
347
348      @Override /* Overridden from Builder */
349      public Builder beanFieldVisibility(Visibility value) {
350         super.beanFieldVisibility(value);
351         return this;
352      }
353
354      @Override /* Overridden from Builder */
355      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
356         super.beanInterceptor(on, value);
357         return this;
358      }
359
360      @Override /* Overridden from Builder */
361      public Builder beanMapPutReturnsOldValue() {
362         super.beanMapPutReturnsOldValue();
363         return this;
364      }
365
366      @Override /* Overridden from Builder */
367      public Builder beanMethodVisibility(Visibility value) {
368         super.beanMethodVisibility(value);
369         return this;
370      }
371
372      @Override /* Overridden from Builder */
373      public Builder beanProperties(Class<?> beanClass, String properties) {
374         super.beanProperties(beanClass, properties);
375         return this;
376      }
377
378      @Override /* Overridden from Builder */
379      public Builder beanProperties(Map<String,Object> values) {
380         super.beanProperties(values);
381         return this;
382      }
383
384      @Override /* Overridden from Builder */
385      public Builder beanProperties(String beanClassName, String properties) {
386         super.beanProperties(beanClassName, properties);
387         return this;
388      }
389
390      @Override /* Overridden from Builder */
391      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
392         super.beanPropertiesExcludes(beanClass, properties);
393         return this;
394      }
395
396      @Override /* Overridden from Builder */
397      public Builder beanPropertiesExcludes(Map<String,Object> values) {
398         super.beanPropertiesExcludes(values);
399         return this;
400      }
401
402      @Override /* Overridden from Builder */
403      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
404         super.beanPropertiesExcludes(beanClassName, properties);
405         return this;
406      }
407
408      @Override /* Overridden from Builder */
409      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
410         super.beanPropertiesReadOnly(beanClass, properties);
411         return this;
412      }
413
414      @Override /* Overridden from Builder */
415      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
416         super.beanPropertiesReadOnly(values);
417         return this;
418      }
419
420      @Override /* Overridden from Builder */
421      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
422         super.beanPropertiesReadOnly(beanClassName, properties);
423         return this;
424      }
425
426      @Override /* Overridden from Builder */
427      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
428         super.beanPropertiesWriteOnly(beanClass, properties);
429         return this;
430      }
431
432      @Override /* Overridden from Builder */
433      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
434         super.beanPropertiesWriteOnly(values);
435         return this;
436      }
437
438      @Override /* Overridden from Builder */
439      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
440         super.beanPropertiesWriteOnly(beanClassName, properties);
441         return this;
442      }
443
444      @Override /* Overridden from Builder */
445      public Builder beansRequireDefaultConstructor() {
446         super.beansRequireDefaultConstructor();
447         return this;
448      }
449
450      @Override /* Overridden from Builder */
451      public Builder beansRequireSerializable() {
452         super.beansRequireSerializable();
453         return this;
454      }
455
456      @Override /* Overridden from Builder */
457      public Builder beansRequireSettersForGetters() {
458         super.beansRequireSettersForGetters();
459         return this;
460      }
461
462      @Override /* Overridden from Context.Builder */
463      public XmlSerializer build() {
464         return cache(CACHE).build(XmlSerializer.class);
465      }
466
467      @Override /* Overridden from Builder */
468      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
469         super.cache(value);
470         return this;
471      }
472
473      @Override /* Overridden from Context.Builder */
474      public Builder copy() {
475         return new Builder(this);
476      }
477
478      @Override /* Overridden from Builder */
479      public Builder debug() {
480         super.debug();
481         return this;
482      }
483
484      @Override /* Overridden from Builder */
485      public Builder debug(boolean value) {
486         super.debug(value);
487         return this;
488      }
489
490      /**
491       * Default namespace.
492       *
493       * <p>
494       * Specifies the default namespace URI for this document.
495       *
496       * <h5 class='section'>See Also:</h5><ul>
497       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a>
498       * </ul>
499       *
500       * @param value
501       *    The new value for this property.
502       *    <br>The default is <js>"juneau: http://www.apache.org/2013/Juneau"</js>.
503       *    <br>Can be <jk>null</jk> to specify no namespace.
504       * @return This object.
505       */
506      public Builder defaultNamespace(Namespace value) {
507         defaultNamespace = value;
508         return this;
509      }
510
511      @Override /* Overridden from Builder */
512      public Builder detectRecursions() {
513         super.detectRecursions();
514         return this;
515      }
516
517      @Override /* Overridden from Builder */
518      public Builder detectRecursions(boolean value) {
519         super.detectRecursions(value);
520         return this;
521      }
522
523      @Override /* Overridden from Builder */
524      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
525         super.dictionaryOn(on, values);
526         return this;
527      }
528
529      /**
530       * Don't auto-detect namespace usage.
531       *
532       * <p>
533       * Don't detect namespace usage before serialization.
534       *
535       * <p>
536       * Used in conjunction with {@link Builder#addNamespaceUrisToRoot()} to reduce the list of namespace URLs appended to the
537       * root element to only those that will be used in the resulting document.
538       *
539       * <p>
540       * If disabled, then the data structure will first be crawled looking for namespaces that will be encountered before
541       * the root element is serialized.
542       *
543       * <p>
544       * This setting is ignored if {@link Builder#enableNamespaces()} is not enabled.
545       *
546       * <h5 class='section'>Notes:</h5><ul>
547       *    <li class='note'>
548       *       Auto-detection of namespaces can be costly performance-wise.
549       *       <br>In high-performance environments, it's recommended that namespace detection be
550       *       disabled, and that namespaces be manually defined through the {@link Builder#namespaces(Namespace...)} property.
551       * </ul>
552       *
553       * <h5 class='section'>See Also:</h5><ul>
554       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a>
555       * </ul>
556       *
557       * @return This object.
558       */
559      public Builder disableAutoDetectNamespaces() {
560         return disableAutoDetectNamespaces(true);
561      }
562
563      /**
564       * Same as {@link #disableAutoDetectNamespaces()} but allows you to explicitly specify the value.
565       *
566       * @param value The value for this setting.
567       * @return This object.
568       */
569      public Builder disableAutoDetectNamespaces(boolean value) {
570         disableAutoDetectNamespaces = value;
571         return this;
572      }
573
574      @Override /* Overridden from Builder */
575      public Builder disableBeansRequireSomeProperties() {
576         super.disableBeansRequireSomeProperties();
577         return this;
578      }
579
580      @Override /* Overridden from Builder */
581      public Builder disableIgnoreMissingSetters() {
582         super.disableIgnoreMissingSetters();
583         return this;
584      }
585
586      @Override /* Overridden from Builder */
587      public Builder disableIgnoreTransientFields() {
588         super.disableIgnoreTransientFields();
589         return this;
590      }
591
592      @Override /* Overridden from Builder */
593      public Builder disableIgnoreUnknownNullBeanProperties() {
594         super.disableIgnoreUnknownNullBeanProperties();
595         return this;
596      }
597
598      @Override /* Overridden from Builder */
599      public Builder disableInterfaceProxies() {
600         super.disableInterfaceProxies();
601         return this;
602      }
603
604      /**
605       * <i><l>XmlSerializer</l> configuration property:&emsp;</i>  Disable use of JSON type identifier tags.
606       *
607       * <p>
608       * When enabled, JSON type tags (e.g. <js>"&lt;string&gt;"</js>) tags and attributes will not be added to the output.
609       * Note that JSON type tags are used to ensure parsers are able to recreate the original data types passed
610       * into the serializer.  Disabling JSON tags can cause different data to be parsed (e.g. strings instead of numbers).
611       *
612       * @return This object.
613       */
614      public Builder disableJsonTags() {
615         return disableJsonTags(true);
616      }
617
618      /**
619       * Same as {@link #disableJsonTags()} but allows you to explicitly specify the value.
620       *
621       * @param value The value for this setting.
622       * @return This object.
623       */
624      public Builder disableJsonTags(boolean value) {
625         disableJsonTags = value;
626         return this;
627      }
628
629      /**
630       * Enable support for XML namespaces.
631       *
632       * <p>
633       * If not enabled, XML output will not contain any namespaces regardless of any other settings.
634       *
635       * <h5 class='section'>See Also:</h5><ul>
636       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a>
637       * </ul>
638       *
639       * @return This object.
640       */
641      public Builder enableNamespaces() {
642         return enableNamespaces(true);
643      }
644
645      /**
646       * Same as {@link #enableNamespaces()} but allows you to explicitly specify the value.
647       *
648       * @param value The value for this setting.
649       * @return This object.
650       */
651      public Builder enableNamespaces(boolean value) {
652         enableNamespaces = value;
653         return this;
654      }
655
656      @Override /* Overridden from Builder */
657      public <T> Builder example(Class<T> pojoClass, String json) {
658         super.example(pojoClass, json);
659         return this;
660      }
661
662      @Override /* Overridden from Builder */
663      public <T> Builder example(Class<T> pojoClass, T o) {
664         super.example(pojoClass, o);
665         return this;
666      }
667
668      @Override /* Overridden from Builder */
669      public Builder fileCharset(Charset value) {
670         super.fileCharset(value);
671         return this;
672      }
673
674      @Override /* Overridden from Builder */
675      public Builder findFluentSetters() {
676         super.findFluentSetters();
677         return this;
678      }
679
680      @Override /* Overridden from Builder */
681      public Builder findFluentSetters(Class<?> on) {
682         super.findFluentSetters(on);
683         return this;
684      }
685
686      @Override /* Overridden from Context.Builder */
687      public HashKey hashKey() {
688         // @formatter:off
689         return HashKey.of(
690            super.hashKey(),
691            addBeanTypesXml,
692            addNamespaceUrisToRoot,
693            disableAutoDetectNamespaces,
694            disableJsonTags,
695            enableNamespaces,
696            defaultNamespace,
697            namespaces,
698            textNodeDelimiter
699         );
700         // @formatter:on
701      }
702
703      @Override /* Overridden from Builder */
704      public Builder ignoreInvocationExceptionsOnGetters() {
705         super.ignoreInvocationExceptionsOnGetters();
706         return this;
707      }
708
709      @Override /* Overridden from Builder */
710      public Builder ignoreInvocationExceptionsOnSetters() {
711         super.ignoreInvocationExceptionsOnSetters();
712         return this;
713      }
714
715      @Override /* Overridden from Builder */
716      public Builder ignoreRecursions() {
717         super.ignoreRecursions();
718         return this;
719      }
720
721      @Override /* Overridden from Builder */
722      public Builder ignoreRecursions(boolean value) {
723         super.ignoreRecursions(value);
724         return this;
725      }
726
727      @Override /* Overridden from Builder */
728      public Builder ignoreUnknownBeanProperties() {
729         super.ignoreUnknownBeanProperties();
730         return this;
731      }
732
733      @Override /* Overridden from Builder */
734      public Builder ignoreUnknownEnumValues() {
735         super.ignoreUnknownEnumValues();
736         return this;
737      }
738
739      @Override /* Overridden from Builder */
740      public Builder impl(Context value) {
741         super.impl(value);
742         return this;
743      }
744
745      @Override /* Overridden from Builder */
746      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
747         super.implClass(interfaceClass, implClass);
748         return this;
749      }
750
751      @Override /* Overridden from Builder */
752      public Builder implClasses(Map<Class<?>,Class<?>> values) {
753         super.implClasses(values);
754         return this;
755      }
756
757      @Override /* Overridden from Builder */
758      public Builder initialDepth(int value) {
759         super.initialDepth(value);
760         return this;
761      }
762
763      @Override /* Overridden from Builder */
764      public Builder interfaceClass(Class<?> on, Class<?> value) {
765         super.interfaceClass(on, value);
766         return this;
767      }
768
769      @Override /* Overridden from Builder */
770      public Builder interfaces(java.lang.Class<?>...value) {
771         super.interfaces(value);
772         return this;
773      }
774
775      @Override /* Overridden from Builder */
776      public Builder keepNullProperties() {
777         super.keepNullProperties();
778         return this;
779      }
780
781      @Override /* Overridden from Builder */
782      public Builder keepNullProperties(boolean value) {
783         super.keepNullProperties(value);
784         return this;
785      }
786
787      @Override /* Overridden from Builder */
788      public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
789         super.listener(value);
790         return this;
791      }
792
793      @Override /* Overridden from Builder */
794      public Builder locale(Locale value) {
795         super.locale(value);
796         return this;
797      }
798
799      @Override /* Overridden from Builder */
800      public Builder maxDepth(int value) {
801         super.maxDepth(value);
802         return this;
803      }
804
805      @Override /* Overridden from Builder */
806      public Builder maxIndent(int value) {
807         super.maxIndent(value);
808         return this;
809      }
810
811      @Override /* Overridden from Builder */
812      public Builder mediaType(MediaType value) {
813         super.mediaType(value);
814         return this;
815      }
816
817      /**
818       * Default namespaces.
819       *
820       * <p>
821       * The default list of namespaces associated with this serializer.
822       *
823       * @param values The new value for this property.
824       *    <br>Cannot contain <jk>null</jk> values.
825       * @return This object.
826       */
827      public Builder namespaces(Namespace...values) {
828         assertArgNoNulls("values", values);
829         namespaces = addAll(namespaces, values);
830         return this;
831      }
832
833      @Override /* Overridden from Builder */
834      public Builder notBeanClasses(java.lang.Class<?>...values) {
835         super.notBeanClasses(values);
836         return this;
837      }
838
839      @Override /* Overridden from Builder */
840      public Builder notBeanPackages(String...values) {
841         super.notBeanPackages(values);
842         return this;
843      }
844
845      /**
846       * Enable support for XML namespaces.
847       *
848       * <p>
849       * Shortcut for calling <code>enableNamespaces(<jk>true</jk>)</code>.
850       *
851       * @return This object.
852       */
853      public Builder ns() {
854         return enableNamespaces();
855      }
856
857      @Override /* Overridden from Builder */
858      public Builder produces(String value) {
859         super.produces(value);
860         return this;
861      }
862
863      @Override /* Overridden from Builder */
864      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
865         super.propertyNamer(on, value);
866         return this;
867      }
868
869      @Override /* Overridden from Builder */
870      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
871         super.propertyNamer(value);
872         return this;
873      }
874
875      @Override /* Overridden from Builder */
876      public Builder quoteChar(char value) {
877         super.quoteChar(value);
878         return this;
879      }
880
881      @Override /* Overridden from Builder */
882      public Builder quoteCharOverride(char value) {
883         super.quoteCharOverride(value);
884         return this;
885      }
886
887      @Override /* Overridden from Builder */
888      public Builder sortCollections() {
889         super.sortCollections();
890         return this;
891      }
892
893      @Override /* Overridden from Builder */
894      public Builder sortCollections(boolean value) {
895         super.sortCollections(value);
896         return this;
897      }
898
899      @Override /* Overridden from Builder */
900      public Builder sortMaps() {
901         super.sortMaps();
902         return this;
903      }
904
905      @Override /* Overridden from Builder */
906      public Builder sortMaps(boolean value) {
907         super.sortMaps(value);
908         return this;
909      }
910
911      @Override /* Overridden from Builder */
912      public Builder sortProperties() {
913         super.sortProperties();
914         return this;
915      }
916
917      @Override /* Overridden from Builder */
918      public Builder sortProperties(java.lang.Class<?>...on) {
919         super.sortProperties(on);
920         return this;
921      }
922
923      @Override /* Overridden from Builder */
924      public Builder sq() {
925         super.sq();
926         return this;
927      }
928
929      @Override /* Overridden from Builder */
930      public Builder stopClass(Class<?> on, Class<?> value) {
931         super.stopClass(on, value);
932         return this;
933      }
934
935      @Override /* Overridden from Builder */
936      public Builder streamCharset(Charset value) {
937         super.streamCharset(value);
938         return this;
939      }
940
941      @Override /* Overridden from Builder */
942      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
943         super.swap(normalClass, swappedClass, swapFunction);
944         return this;
945      }
946
947      @Override /* Overridden from Builder */
948      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
949         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
950         return this;
951      }
952
953      @Override /* Overridden from Builder */
954      public Builder swaps(Class<?>...values) {
955         super.swaps(values);
956         return this;
957      }
958
959      @Override /* Overridden from Builder */
960      public Builder swaps(Object...values) {
961         super.swaps(values);
962         return this;
963      }
964
965      /**
966       * Text node delimiter.
967       *
968       * <p>
969       * Specifies the delimiter string to insert between consecutive text nodes.
970       * This is useful for adding spacing between text elements to improve readability.
971       *
972       * <p>
973       * The default value is an empty string (no delimiter).
974       *
975       * <h5 class='section'>Example:</h5>
976       * <p class='bjava'>
977       *    XmlSerializer.<jsm>create</jsm>()
978       *       .textNodeDelimiter(<js>" "</js>)
979       *       .build();
980       * </p>
981       *
982       * <p>
983       * With this setting, serializing:
984       * <p class='bjava'>
985       *    <jk>new</jk> Audio().children(<js>"a"</js>, <js>"b"</js>, <jk>new</jk> Strong(<js>"c"</js>));
986       * </p>
987       *
988       * <p>
989       * Will produce: <code>&lt;audio&gt;a b&lt;strong&gt;c&lt;/strong&gt;&lt;/audio&gt;</code>
990       * <br>Instead of: <code>&lt;audio&gt;ab&lt;strong&gt;c&lt;/strong&gt;&lt;/audio&gt;</code>
991       *
992       * @param value
993       *    The delimiter string.
994       *    <br>Can be <jk>null</jk> (interpreted as empty string).
995       * @return This object.
996       */
997      public Builder textNodeDelimiter(String value) {
998         textNodeDelimiter = value == null ? "" : value;
999         return this;
1000      }
1001
1002      @Override /* Overridden from Builder */
1003      public Builder timeZone(TimeZone value) {
1004         super.timeZone(value);
1005         return this;
1006      }
1007
1008      @Override /* Overridden from Builder */
1009      public Builder trimEmptyCollections() {
1010         super.trimEmptyCollections();
1011         return this;
1012      }
1013
1014      @Override /* Overridden from Builder */
1015      public Builder trimEmptyCollections(boolean value) {
1016         super.trimEmptyCollections(value);
1017         return this;
1018      }
1019
1020      @Override /* Overridden from Builder */
1021      public Builder trimEmptyMaps() {
1022         super.trimEmptyMaps();
1023         return this;
1024      }
1025
1026      @Override /* Overridden from Builder */
1027      public Builder trimEmptyMaps(boolean value) {
1028         super.trimEmptyMaps(value);
1029         return this;
1030      }
1031
1032      @Override /* Overridden from Builder */
1033      public Builder trimStrings() {
1034         super.trimStrings();
1035         return this;
1036      }
1037
1038      @Override /* Overridden from Builder */
1039      public Builder trimStrings(boolean value) {
1040         super.trimStrings(value);
1041         return this;
1042      }
1043
1044      @Override /* Overridden from Builder */
1045      public Builder type(Class<? extends org.apache.juneau.Context> value) {
1046         super.type(value);
1047         return this;
1048      }
1049
1050      @Override /* Overridden from Builder */
1051      public Builder typeName(Class<?> on, String value) {
1052         super.typeName(on, value);
1053         return this;
1054      }
1055
1056      @Override /* Overridden from Builder */
1057      public Builder typePropertyName(Class<?> on, String value) {
1058         super.typePropertyName(on, value);
1059         return this;
1060      }
1061
1062      @Override /* Overridden from Builder */
1063      public Builder typePropertyName(String value) {
1064         super.typePropertyName(value);
1065         return this;
1066      }
1067
1068      @Override /* Overridden from Builder */
1069      public Builder uriContext(UriContext value) {
1070         super.uriContext(value);
1071         return this;
1072      }
1073
1074      @Override /* Overridden from Builder */
1075      public Builder uriRelativity(UriRelativity value) {
1076         super.uriRelativity(value);
1077         return this;
1078      }
1079
1080      @Override /* Overridden from Builder */
1081      public Builder uriResolution(UriResolution value) {
1082         super.uriResolution(value);
1083         return this;
1084      }
1085
1086      @Override /* Overridden from Builder */
1087      public Builder useEnumNames() {
1088         super.useEnumNames();
1089         return this;
1090      }
1091
1092      @Override /* Overridden from Builder */
1093      public Builder useJavaBeanIntrospector() {
1094         super.useJavaBeanIntrospector();
1095         return this;
1096      }
1097
1098      @Override /* Overridden from Builder */
1099      public Builder useWhitespace() {
1100         super.useWhitespace();
1101         return this;
1102      }
1103
1104      @Override /* Overridden from Builder */
1105      public Builder useWhitespace(boolean value) {
1106         super.useWhitespace(value);
1107         return this;
1108      }
1109
1110      @Override /* Overridden from Builder */
1111      public Builder ws() {
1112         super.ws();
1113         return this;
1114      }
1115   }
1116
1117   /** Default serializer without namespaces. */
1118   public static class Ns extends XmlSerializer {
1119
1120      /**
1121       * Constructor.
1122       *
1123       * @param builder The builder for this object.
1124       *    <br>Cannot be <jk>null</jk>.
1125       */
1126      public Ns(Builder builder) {
1127         super(assertArgNotNull("builder", builder).enableNamespaces());
1128      }
1129   }
1130
1131   /** Default serializer without namespaces, single quotes. */
1132   public static class NsSq extends XmlSerializer {
1133
1134      /**
1135       * Constructor.
1136       *
1137       * @param builder The builder for this object.
1138       *    <br>Cannot be <jk>null</jk>.
1139       */
1140      public NsSq(Builder builder) {
1141         super(assertArgNotNull("builder", builder).enableNamespaces().quoteChar('\''));
1142      }
1143   }
1144
1145   /** Default serializer without namespaces, single quotes, with whitespace. */
1146   public static class NsSqReadable extends XmlSerializer {
1147
1148      /**
1149       * Constructor.
1150       *
1151       * @param builder The builder for this object.
1152       *    <br>Cannot be <jk>null</jk>.
1153       */
1154      public NsSqReadable(Builder builder) {
1155         super(assertArgNotNull("builder", builder).enableNamespaces().quoteChar('\'').useWhitespace());
1156      }
1157   }
1158
1159   /** Default serializer, single quotes. */
1160   public static class Sq extends XmlSerializer {
1161
1162      /**
1163       * Constructor.
1164       *
1165       * @param builder The builder for this object.
1166       *    <br>Cannot be <jk>null</jk>.
1167       */
1168      public Sq(Builder builder) {
1169         super(assertArgNotNull("builder", builder).quoteChar('\''));
1170      }
1171   }
1172
1173   /** Default serializer, single quotes, whitespace added. */
1174   public static class SqReadable extends XmlSerializer {
1175
1176      /**
1177       * Constructor.
1178       *
1179       * @param builder The builder for this object.
1180       *    <br>Cannot be <jk>null</jk>.
1181       */
1182      public SqReadable(Builder builder) {
1183         super(assertArgNotNull("builder", builder).quoteChar('\'').useWhitespace());
1184      }
1185   }
1186
1187
1188   /** Default serializer without namespaces. */
1189   public static final XmlSerializer DEFAULT = new XmlSerializer(create());
1190   /** Default serializer without namespaces, with single quotes. */
1191   public static final XmlSerializer DEFAULT_SQ = new Sq(create());
1192
1193   /** Default serializer without namespaces, with single quotes, whitespace added. */
1194   public static final XmlSerializer DEFAULT_SQ_READABLE = new SqReadable(create());
1195
1196   /** Default serializer, all default settings. */
1197   public static final XmlSerializer DEFAULT_NS = new Ns(create());
1198
1199   /** Default serializer, single quotes. */
1200   public static final XmlSerializer DEFAULT_NS_SQ = new NsSq(create());
1201
1202   /** Default serializer, single quotes, whitespace added. */
1203   public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new NsSqReadable(create());
1204
1205   protected static final Namespace DEFAULT_JUNEAU_NAMESPACE = Namespace.of("juneau", "http://www.apache.org/2013/Juneau"),
1206      DEFAULT_XS_NAMESPACE = Namespace.of("xs", "http://www.w3.org/2001/XMLSchema");
1207
1208   /**
1209    * Creates a new builder for this object.
1210    *
1211    * @return A new builder.
1212    */
1213   public static Builder create() {
1214      return new Builder();
1215   }
1216
1217   protected final boolean addBeanTypesXml;
1218   protected final boolean addJsonTags;
1219   protected final boolean addNamespaceUrlsToRoot;
1220   protected final boolean autoDetectNamespaces;
1221   protected final boolean enableNamespaces;
1222   private final Namespace defaultNamespace;
1223   private final List<Namespace> namespaces;
1224   private final String textNodeDelimiter;
1225
1226   private final boolean addBeanTypes;
1227   private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
1228   private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
1229   private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
1230
1231   /**
1232    * Constructor.
1233    *
1234    * @param builder
1235    *    The builder for this object.
1236    */
1237   public XmlSerializer(Builder builder) {
1238      super(builder);
1239      addBeanTypesXml = builder.addBeanTypesXml;
1240      addJsonTags = ! builder.disableJsonTags;
1241      addNamespaceUrlsToRoot = builder.addNamespaceUrisToRoot;
1242      autoDetectNamespaces = ! builder.disableAutoDetectNamespaces;
1243      defaultNamespace = nn(builder.defaultNamespace) ? builder.defaultNamespace : DEFAULT_JUNEAU_NAMESPACE;
1244      enableNamespaces = builder.enableNamespaces;
1245      namespaces = u(nn(builder.namespaces) ? new ArrayList<>(builder.namespaces) : new ArrayList<>());
1246      textNodeDelimiter = builder.textNodeDelimiter;
1247      addBeanTypes = addBeanTypesXml || super.isAddBeanTypes();
1248   }
1249
1250   @Override /* Overridden from Context */
1251   public Builder copy() {
1252      return new Builder(this);
1253   }
1254
1255   @Override /* Overridden from Context */
1256   public XmlSerializerSession.Builder createSession() {
1257      return XmlSerializerSession.create(this);
1258   }
1259
1260   @Override /* Overridden from Context */
1261   public XmlSerializerSession getSession() { return createSession().build(); }
1262
1263   @Override /* Overridden from XmlMetaProvider */
1264   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
1265      XmlBeanMeta m = xmlBeanMetas.get(bm);
1266      if (m == null) {
1267         m = new XmlBeanMeta(bm, this);
1268         xmlBeanMetas.put(bm, m);
1269      }
1270      return m;
1271   }
1272
1273   @Override /* Overridden from XmlMetaProvider */
1274   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
1275      XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
1276      if (m == null) {
1277         m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
1278         xmlBeanPropertyMetas.put(bpm, m);
1279      }
1280      return m;
1281   }
1282
1283   @Override /* Overridden from XmlMetaProvider */
1284   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
1285      XmlClassMeta m = xmlClassMetas.get(cm);
1286      if (m == null) {
1287         m = new XmlClassMeta(cm, this);
1288         xmlClassMetas.put(cm, m);
1289      }
1290      return m;
1291   }
1292
1293   /**
1294    * Default namespace.
1295    *
1296    * @see Builder#defaultNamespace(Namespace)
1297    * @return
1298    *    The default namespace URI for this document.
1299    */
1300   protected final Namespace getDefaultNamespace() { return defaultNamespace; }
1301
1302   /**
1303    * Default namespaces.
1304    *
1305    * @see Builder#namespaces(Namespace...)
1306    * @return
1307    *    The default list of namespaces associated with this serializer.
1308    *    <br>Never <jk>null</jk>.
1309    *    <br>List is unmodifiable.
1310    */
1311   protected final List<Namespace> getNamespaces() { return namespaces; }
1312
1313   /**
1314    * Text node delimiter.
1315    *
1316    * @see Builder#textNodeDelimiter(String)
1317    * @return
1318    *    The delimiter string to insert between consecutive text nodes.
1319    *    <br>Never <jk>null</jk>.
1320    */
1321   protected final String getTextNodeDelimiter() { return textNodeDelimiter; }
1322
1323   /**
1324    * Add <js>"_type"</js> properties when needed.
1325    *
1326    * @see Builder#addBeanTypesXml()
1327    * @return
1328    *    <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred
1329    *    through reflection.
1330    */
1331   @Override
1332   protected boolean isAddBeanTypes() { return addBeanTypes; }
1333
1334   /**
1335    * Add namespace URLs to the root element.
1336    *
1337    * @see Builder#addNamespaceUrisToRoot()
1338    * @return
1339    *    <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces.
1340    */
1341   protected final boolean isAddNamespaceUrlsToRoot() { return addNamespaceUrlsToRoot; }
1342
1343   /**
1344    * Auto-detect namespace usage.
1345    *
1346    * @see Builder#disableAutoDetectNamespaces()
1347    * @return
1348    *    <jk>true</jk> if namespace usage is detected before serialization.
1349    */
1350   protected final boolean isAutoDetectNamespaces() { return autoDetectNamespaces; }
1351
1352   /**
1353    * Enable support for XML namespaces.
1354    *
1355    * @see Builder#enableNamespaces()
1356    * @return
1357    *    <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings.
1358    */
1359   protected final boolean isEnableNamespaces() { return enableNamespaces; }
1360
1361   @Override /* Overridden from WriterSerializer */
1362   protected FluentMap<String,Object> properties() {
1363      return super.properties()
1364         .a("addBeanTypes", addBeanTypes)
1365         .a("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot)
1366         .a("autoDetectNamespaces", autoDetectNamespaces)
1367         .a("defaultNamespace", defaultNamespace)
1368         .a("enableNamespaces", enableNamespaces)
1369         .a("namespaces", namespaces);
1370   }
1371}