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.serializer;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.IoUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023
024import java.lang.annotation.*;
025import java.nio.charset.*;
026import java.util.*;
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.*;
033
034/**
035 * Subclass of {@link Serializer} for character-based serializers.
036 *
037 * <h5 class='section'>Notes:</h5><ul>
038 *    <li class='note'>This class is thread safe and reusable.
039 * </ul>
040 *
041 * <h5 class='section'>See Also:</h5><ul>
042 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
043 * </ul>
044 */
045public class WriterSerializer extends Serializer {
046   /**
047    * Builder class.
048    */
049   public static class Builder extends Serializer.Builder {
050
051      private boolean useWhitespace;
052      private Character quoteChar;
053      private Character quoteCharOverride;
054      private Charset fileCharset;
055      private Charset streamCharset;
056      private int maxIndent;
057
058      /**
059       * Constructor, default settings.
060       */
061      protected Builder() {
062         fileCharset = Charset.defaultCharset();
063         streamCharset = UTF8;
064         maxIndent = env("WriterSerializer.maxIndent", 100);
065         quoteChar = env("WriterSerializer.quoteChar").map(x -> (x.length() > 0 ? x.charAt(0) : null)).orElse(null);
066         quoteCharOverride = env("WriterSerializer.quoteCharOverride").map(x -> (x.length() > 0 ? x.charAt(0) : null)).orElse(null);
067         useWhitespace = env("WriterSerializer.useWhitespace", false);
068      }
069
070      /**
071       * Copy constructor.
072       *
073       * @param copyFrom The builder to copy from.
074       *    <br>Cannot be <jk>null</jk>.
075       */
076      protected Builder(Builder copyFrom) {
077         super(assertArgNotNull("copyFrom", copyFrom));
078         fileCharset = copyFrom.fileCharset;
079         streamCharset = copyFrom.streamCharset;
080         maxIndent = copyFrom.maxIndent;
081         quoteChar = copyFrom.quoteChar;
082         quoteCharOverride = copyFrom.quoteCharOverride;
083         useWhitespace = copyFrom.useWhitespace;
084      }
085
086      /**
087       * Copy constructor.
088       *
089       * @param copyFrom The bean to copy from.
090       *    <br>Cannot be <jk>null</jk>.
091       */
092      protected Builder(WriterSerializer copyFrom) {
093         super(assertArgNotNull("copyFrom", copyFrom));
094         fileCharset = copyFrom.getFileCharset();
095         streamCharset = copyFrom.getStreamCharset();
096         maxIndent = copyFrom.maxIndent;
097         quoteChar = copyFrom.quoteChar;
098         quoteCharOverride = copyFrom.quoteCharOverride;
099         useWhitespace = copyFrom.useWhitespace;
100      }
101
102      @Override /* Overridden from Builder */
103      public Builder accept(String value) {
104         super.accept(value);
105         return this;
106      }
107
108      @Override /* Overridden from Builder */
109      public Builder addBeanTypes() {
110         super.addBeanTypes();
111         return this;
112      }
113
114      @Override /* Overridden from Builder */
115      public Builder addBeanTypes(boolean value) {
116         super.addBeanTypes(value);
117         return this;
118      }
119
120      @Override /* Overridden from Builder */
121      public Builder addRootType() {
122         super.addRootType();
123         return this;
124      }
125
126      @Override /* Overridden from Builder */
127      public Builder addRootType(boolean value) {
128         super.addRootType(value);
129         return this;
130      }
131
132      @Override /* Overridden from Builder */
133      public Builder annotations(Annotation...values) {
134         super.annotations(values);
135         return this;
136      }
137
138      @Override /* Overridden from Builder */
139      public Builder apply(AnnotationWorkList work) {
140         super.apply(work);
141         return this;
142      }
143
144      @Override /* Overridden from Builder */
145      public Builder applyAnnotations(Class<?>...from) {
146         super.applyAnnotations(from);
147         return this;
148      }
149
150      @Override /* Overridden from Builder */
151      public Builder applyAnnotations(Object...from) {
152         super.applyAnnotations(from);
153         return this;
154      }
155
156      @Override /* Overridden from Builder */
157      public Builder beanClassVisibility(Visibility value) {
158         super.beanClassVisibility(value);
159         return this;
160      }
161
162      @Override /* Overridden from Builder */
163      public Builder beanConstructorVisibility(Visibility value) {
164         super.beanConstructorVisibility(value);
165         return this;
166      }
167
168      @Override /* Overridden from Builder */
169      public Builder beanContext(BeanContext value) {
170         super.beanContext(value);
171         return this;
172      }
173
174      @Override /* Overridden from Builder */
175      public Builder beanContext(BeanContext.Builder value) {
176         super.beanContext(value);
177         return this;
178      }
179
180      @Override /* Overridden from Builder */
181      public Builder beanDictionary(java.lang.Class<?>...values) {
182         super.beanDictionary(values);
183         return this;
184      }
185
186      @Override /* Overridden from Builder */
187      public Builder beanFieldVisibility(Visibility value) {
188         super.beanFieldVisibility(value);
189         return this;
190      }
191
192      @Override /* Overridden from Builder */
193      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
194         super.beanInterceptor(on, value);
195         return this;
196      }
197
198      @Override /* Overridden from Builder */
199      public Builder beanMapPutReturnsOldValue() {
200         super.beanMapPutReturnsOldValue();
201         return this;
202      }
203
204      @Override /* Overridden from Builder */
205      public Builder beanMethodVisibility(Visibility value) {
206         super.beanMethodVisibility(value);
207         return this;
208      }
209
210      @Override /* Overridden from Builder */
211      public Builder beanProperties(Class<?> beanClass, String properties) {
212         super.beanProperties(beanClass, properties);
213         return this;
214      }
215
216      @Override /* Overridden from Builder */
217      public Builder beanProperties(Map<String,Object> values) {
218         super.beanProperties(values);
219         return this;
220      }
221
222      @Override /* Overridden from Builder */
223      public Builder beanProperties(String beanClassName, String properties) {
224         super.beanProperties(beanClassName, properties);
225         return this;
226      }
227
228      @Override /* Overridden from Builder */
229      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
230         super.beanPropertiesExcludes(beanClass, properties);
231         return this;
232      }
233
234      @Override /* Overridden from Builder */
235      public Builder beanPropertiesExcludes(Map<String,Object> values) {
236         super.beanPropertiesExcludes(values);
237         return this;
238      }
239
240      @Override /* Overridden from Builder */
241      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
242         super.beanPropertiesExcludes(beanClassName, properties);
243         return this;
244      }
245
246      @Override /* Overridden from Builder */
247      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
248         super.beanPropertiesReadOnly(beanClass, properties);
249         return this;
250      }
251
252      @Override /* Overridden from Builder */
253      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
254         super.beanPropertiesReadOnly(values);
255         return this;
256      }
257
258      @Override /* Overridden from Builder */
259      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
260         super.beanPropertiesReadOnly(beanClassName, properties);
261         return this;
262      }
263
264      @Override /* Overridden from Builder */
265      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
266         super.beanPropertiesWriteOnly(beanClass, properties);
267         return this;
268      }
269
270      @Override /* Overridden from Builder */
271      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
272         super.beanPropertiesWriteOnly(values);
273         return this;
274      }
275
276      @Override /* Overridden from Builder */
277      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
278         super.beanPropertiesWriteOnly(beanClassName, properties);
279         return this;
280      }
281
282      @Override /* Overridden from Builder */
283      public Builder beansRequireDefaultConstructor() {
284         super.beansRequireDefaultConstructor();
285         return this;
286      }
287
288      @Override /* Overridden from Builder */
289      public Builder beansRequireSerializable() {
290         super.beansRequireSerializable();
291         return this;
292      }
293
294      @Override /* Overridden from Builder */
295      public Builder beansRequireSettersForGetters() {
296         super.beansRequireSettersForGetters();
297         return this;
298      }
299
300      @Override /* Overridden from Context.Builder */
301      public WriterSerializer build() {
302         return build(WriterSerializer.class);
303      }
304
305      @Override /* Overridden from Builder */
306      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
307         super.cache(value);
308         return this;
309      }
310
311      @Override /* Overridden from Context.Builder */
312      public Builder copy() {
313         return new Builder(this);
314      }
315
316      @Override /* Overridden from Builder */
317      public Builder debug() {
318         super.debug();
319         return this;
320      }
321
322      @Override /* Overridden from Builder */
323      public Builder debug(boolean value) {
324         super.debug(value);
325         return this;
326      }
327
328      @Override /* Overridden from Builder */
329      public Builder detectRecursions() {
330         super.detectRecursions();
331         return this;
332      }
333
334      @Override /* Overridden from Builder */
335      public Builder detectRecursions(boolean value) {
336         super.detectRecursions(value);
337         return this;
338      }
339
340      @Override /* Overridden from Builder */
341      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
342         super.dictionaryOn(on, values);
343         return this;
344      }
345
346      @Override /* Overridden from Builder */
347      public Builder disableBeansRequireSomeProperties() {
348         super.disableBeansRequireSomeProperties();
349         return this;
350      }
351
352      @Override /* Overridden from Builder */
353      public Builder disableIgnoreMissingSetters() {
354         super.disableIgnoreMissingSetters();
355         return this;
356      }
357
358      @Override /* Overridden from Builder */
359      public Builder disableIgnoreTransientFields() {
360         super.disableIgnoreTransientFields();
361         return this;
362      }
363
364      @Override /* Overridden from Builder */
365      public Builder disableIgnoreUnknownNullBeanProperties() {
366         super.disableIgnoreUnknownNullBeanProperties();
367         return this;
368      }
369
370      @Override /* Overridden from Builder */
371      public Builder disableInterfaceProxies() {
372         super.disableInterfaceProxies();
373         return this;
374      }
375
376      @Override /* Overridden from Builder */
377      public <T> Builder example(Class<T> pojoClass, String json) {
378         super.example(pojoClass, json);
379         return this;
380      }
381
382      @Override /* Overridden from Builder */
383      public <T> Builder example(Class<T> pojoClass, T o) {
384         super.example(pojoClass, o);
385         return this;
386      }
387
388      /**
389       * File charset.
390       *
391       * <p>
392       * The character set to use for writing <c>Files</c> to the file system.
393       *
394       * <p>
395       * Used when passing in files to {@link Serializer#serialize(Object, Object)}.
396       *
397       * <h5 class='section'>Example:</h5>
398       * <p class='bjava'>
399       *    <jc>// Create a serializer that writes UTF-8 files.</jc>
400       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
401       *       .<jsm>create</jsm>()
402       *       .fileCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>))
403       *       .build();
404       *
405       *    <jc>// Use it to read a UTF-8 encoded file.</jc>
406       *    <jv>serializer</jv>.serialize(<jk>new</jk> File(<js>"MyBean.txt"</js>), <jv>myBean</jv>);
407       * </p>
408       *
409       * @param value
410       *    The new value for this property.
411       *    <br>The default is the system JVM setting.
412       *    <br>Can be <jk>null</jk> (defaults to system default).
413       * @return This object.
414       */
415      public Builder fileCharset(Charset value) {
416         fileCharset = value;
417         return this;
418      }
419
420      @Override /* Overridden from Builder */
421      public Builder findFluentSetters() {
422         super.findFluentSetters();
423         return this;
424      }
425
426      @Override /* Overridden from Builder */
427      public Builder findFluentSetters(Class<?> on) {
428         super.findFluentSetters(on);
429         return this;
430      }
431
432      @Override /* Overridden from Context.Builder */
433      public HashKey hashKey() {
434         // @formatter:off
435         return HashKey.of(
436            super.hashKey(),
437            fileCharset,
438            streamCharset,
439            maxIndent,
440            quoteChar,
441            quoteCharOverride,
442            useWhitespace
443         );
444         // @formatter:on
445      }
446
447      @Override /* Overridden from Builder */
448      public Builder ignoreInvocationExceptionsOnGetters() {
449         super.ignoreInvocationExceptionsOnGetters();
450         return this;
451      }
452
453      @Override /* Overridden from Builder */
454      public Builder ignoreInvocationExceptionsOnSetters() {
455         super.ignoreInvocationExceptionsOnSetters();
456         return this;
457      }
458
459      @Override /* Overridden from Builder */
460      public Builder ignoreRecursions() {
461         super.ignoreRecursions();
462         return this;
463      }
464
465      @Override /* Overridden from Builder */
466      public Builder ignoreRecursions(boolean value) {
467         super.ignoreRecursions(value);
468         return this;
469      }
470
471      @Override /* Overridden from Builder */
472      public Builder ignoreUnknownBeanProperties() {
473         super.ignoreUnknownBeanProperties();
474         return this;
475      }
476
477      @Override /* Overridden from Builder */
478      public Builder ignoreUnknownEnumValues() {
479         super.ignoreUnknownEnumValues();
480         return this;
481      }
482
483      @Override /* Overridden from Builder */
484      public Builder impl(Context value) {
485         super.impl(value);
486         return this;
487      }
488
489      @Override /* Overridden from Builder */
490      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
491         super.implClass(interfaceClass, implClass);
492         return this;
493      }
494
495      @Override /* Overridden from Builder */
496      public Builder implClasses(Map<Class<?>,Class<?>> values) {
497         super.implClasses(values);
498         return this;
499      }
500
501      @Override /* Overridden from Builder */
502      public Builder initialDepth(int value) {
503         super.initialDepth(value);
504         return this;
505      }
506
507      @Override /* Overridden from Builder */
508      public Builder interfaceClass(Class<?> on, Class<?> value) {
509         super.interfaceClass(on, value);
510         return this;
511      }
512
513      @Override /* Overridden from Builder */
514      public Builder interfaces(java.lang.Class<?>...value) {
515         super.interfaces(value);
516         return this;
517      }
518
519      @Override /* Overridden from Builder */
520      public Builder keepNullProperties() {
521         super.keepNullProperties();
522         return this;
523      }
524
525      @Override /* Overridden from Builder */
526      public Builder keepNullProperties(boolean value) {
527         super.keepNullProperties(value);
528         return this;
529      }
530
531      @Override /* Overridden from Builder */
532      public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
533         super.listener(value);
534         return this;
535      }
536
537      @Override /* Overridden from Builder */
538      public Builder locale(Locale value) {
539         super.locale(value);
540         return this;
541      }
542
543      @Override /* Overridden from Builder */
544      public Builder maxDepth(int value) {
545         super.maxDepth(value);
546         return this;
547      }
548
549      /**
550       * Maximum indentation.
551       *
552       * <p>
553       * Specifies the maximum indentation level in the serialized document.
554       *
555       * <h5 class='section'>Notes:</h5><ul>
556       *    <li class='note'>This setting does not apply to the RDF serializers.
557       * </ul>
558       *
559       * <h5 class='section'>Example:</h5>
560       * <p class='bjava'>
561       *    <jc>// Create a serializer that indents a maximum of 20 tabs.</jc>
562       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
563       *       .<jsm>create</jsm>()
564       *       .ws()  <jc>// Enable whitespace</jc>
565       *       .maxIndent(20)
566       *       .build();
567       * </p>
568       *
569       * @param value
570       *    The new value for this property.
571       *    <br>The default is <c>100</c>.
572       * @return This object.
573       */
574      public Builder maxIndent(int value) {
575         maxIndent = value;
576         return this;
577      }
578
579      @Override /* Overridden from Builder */
580      public Builder mediaType(MediaType value) {
581         super.mediaType(value);
582         return this;
583      }
584
585      @Override /* Overridden from Builder */
586      public Builder notBeanClasses(java.lang.Class<?>...values) {
587         super.notBeanClasses(values);
588         return this;
589      }
590
591      @Override /* Overridden from Builder */
592      public Builder notBeanPackages(String...values) {
593         super.notBeanPackages(values);
594         return this;
595      }
596
597      @Override /* Overridden from Builder */
598      public Builder produces(String value) {
599         super.produces(value);
600         return this;
601      }
602
603      @Override /* Overridden from Builder */
604      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
605         super.propertyNamer(on, value);
606         return this;
607      }
608
609      @Override /* Overridden from Builder */
610      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
611         super.propertyNamer(value);
612         return this;
613      }
614
615      /**
616       *  Quote character.
617       *
618       * <p>
619       * Specifies the character to use for quoting attributes and values.
620       *
621       * <h5 class='section'>Notes:</h5><ul>
622       *    <li class='note'>This setting does not apply to the RDF serializers.
623       * </ul>
624       *
625       * <h5 class='section'>Example:</h5>
626       * <p class='bjava'>
627       *    <jc>// Create a serializer that uses single quotes.</jc>
628       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
629       *       .<jsm>create</jsm>()
630       *       .quoteChar(<js>'\''</js>)
631       *       .build();
632       *
633       *    <jc>// A bean with a single property</jc>
634       *    <jk>public class</jk> MyBean {
635       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
636       *    }
637       *
638       *    <jc>// Produces {'foo':'bar'}</jc>
639       *    String <jv>json</jv> = <jv>serializer</jv>.toString(<jk>new</jk> MyBean());
640       * </p>
641       *
642       * @param value
643       *    The new value for this property.
644       *    <br>The default is <js>'"'</js>.
645       * @return This object.
646       */
647      public Builder quoteChar(char value) {
648         quoteChar = value;
649         return this;
650      }
651
652      /**
653       * Quote character override.
654       *
655       * <p>
656       * Similar to {@link #quoteChar(char)} but takes precedence over that setting.
657       *
658       * <p>
659       * Allows you to override the quote character even if it's set by a subclass such as {@link Json5Serializer}.
660       *
661       *
662       * @param value
663       *    The new value for this property.
664       *    <br>The default is <jk>null</jk>.
665       * @return This object.
666       */
667      public Builder quoteCharOverride(char value) {
668         quoteCharOverride = value;
669         return this;
670      }
671
672      @Override /* Overridden from Builder */
673      public Builder sortCollections() {
674         super.sortCollections();
675         return this;
676      }
677
678      @Override /* Overridden from Builder */
679      public Builder sortCollections(boolean value) {
680         super.sortCollections(value);
681         return this;
682      }
683
684      @Override /* Overridden from Builder */
685      public Builder sortMaps() {
686         super.sortMaps();
687         return this;
688      }
689
690      @Override /* Overridden from Builder */
691      public Builder sortMaps(boolean value) {
692         super.sortMaps(value);
693         return this;
694      }
695
696      @Override /* Overridden from Builder */
697      public Builder sortProperties() {
698         super.sortProperties();
699         return this;
700      }
701
702      @Override /* Overridden from Builder */
703      public Builder sortProperties(java.lang.Class<?>...on) {
704         super.sortProperties(on);
705         return this;
706      }
707
708      /**
709       *  Quote character.
710       *
711       * <p>
712       * Specifies to use single quotes for quoting attributes and values.
713       *
714       * <h5 class='section'>Notes:</h5><ul>
715       *    <li class='note'>This setting does not apply to the RDF serializers.
716       * </ul>
717       *
718       * <h5 class='section'>Example:</h5>
719       * <p class='bjava'>
720       *    <jc>// Create a serializer that uses single quotes.</jc>
721       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
722       *       .<jsm>create</jsm>()
723       *       .sq()
724       *       .build();
725       *
726       *    <jc>// A bean with a single property</jc>
727       *    <jk>public class</jk> MyBean {
728       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
729       *    }
730       *
731       *    <jc>// Produces {'foo':'bar'}</jc>
732       *    String <jv>json</jv> = <jv>serializer</jv>.toString(<jk>new</jk> MyBean());
733       * </p>
734       *
735       * @return This object.
736       */
737      public Builder sq() {
738         return quoteChar('\'');
739      }
740
741      @Override /* Overridden from Builder */
742      public Builder stopClass(Class<?> on, Class<?> value) {
743         super.stopClass(on, value);
744         return this;
745      }
746
747      /**
748       * Output stream charset.
749       *
750       * <p>
751       * The character set to use when writing to <c>OutputStreams</c>.
752       *
753       * <p>
754       * Used when passing in output streams and byte arrays to {@link WriterSerializer#serialize(Object, Object)}.
755       *
756       * <h5 class='section'>Example:</h5>
757       * <p class='bjava'>
758       *    <jc>// Create a serializer that writes UTF-8 files.</jc>
759       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
760       *       .<jsm>create</jsm>()
761       *       .streamCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>))
762       *       .build();
763       *
764       *    <jc>// Use it to write to a UTF-8 encoded output stream.</jc>
765       *    <jv>serializer</jv>.serializer(<jk>new</jk> FileOutputStreamStream(<js>"MyBean.txt"</js>), <jv>myBean</jv>);
766       * </p>
767       *
768       * @param value
769       *    The new value for this property.
770       *    <br>The default is the system JVM setting.
771       *    <br>Can be <jk>null</jk> (defaults to UTF-8).
772       * @return This object.
773       */
774      public Builder streamCharset(Charset value) {
775         streamCharset = value;
776         return this;
777      }
778
779      @Override /* Overridden from Builder */
780      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
781         super.swap(normalClass, swappedClass, swapFunction);
782         return this;
783      }
784
785      @Override /* Overridden from Builder */
786      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
787         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
788         return this;
789      }
790
791      @Override /* Overridden from Builder */
792      public Builder swaps(Class<?>...values) {
793         super.swaps(values);
794         return this;
795      }
796
797      @Override /* Overridden from Builder */
798      public Builder swaps(Object...values) {
799         super.swaps(values);
800         return this;
801      }
802
803      @Override /* Overridden from Builder */
804      public Builder timeZone(TimeZone value) {
805         super.timeZone(value);
806         return this;
807      }
808
809      @Override /* Overridden from Builder */
810      public Builder trimEmptyCollections() {
811         super.trimEmptyCollections();
812         return this;
813      }
814
815      @Override /* Overridden from Builder */
816      public Builder trimEmptyCollections(boolean value) {
817         super.trimEmptyCollections(value);
818         return this;
819      }
820
821      @Override /* Overridden from Builder */
822      public Builder trimEmptyMaps() {
823         super.trimEmptyMaps();
824         return this;
825      }
826
827      @Override /* Overridden from Builder */
828      public Builder trimEmptyMaps(boolean value) {
829         super.trimEmptyMaps(value);
830         return this;
831      }
832
833      @Override /* Overridden from Builder */
834      public Builder trimStrings() {
835         super.trimStrings();
836         return this;
837      }
838
839      @Override /* Overridden from Builder */
840      public Builder trimStrings(boolean value) {
841         super.trimStrings(value);
842         return this;
843      }
844
845      @Override /* Overridden from Builder */
846      public Builder type(Class<? extends org.apache.juneau.Context> value) {
847         super.type(value);
848         return this;
849      }
850
851      @Override /* Overridden from Builder */
852      public Builder typeName(Class<?> on, String value) {
853         super.typeName(on, value);
854         return this;
855      }
856
857      @Override /* Overridden from Builder */
858      public Builder typePropertyName(Class<?> on, String value) {
859         super.typePropertyName(on, value);
860         return this;
861      }
862
863      @Override /* Overridden from Builder */
864      public Builder typePropertyName(String value) {
865         super.typePropertyName(value);
866         return this;
867      }
868
869      @Override /* Overridden from Builder */
870      public Builder uriContext(UriContext value) {
871         super.uriContext(value);
872         return this;
873      }
874
875      @Override /* Overridden from Builder */
876      public Builder uriRelativity(UriRelativity value) {
877         super.uriRelativity(value);
878         return this;
879      }
880
881      @Override /* Overridden from Builder */
882      public Builder uriResolution(UriResolution value) {
883         super.uriResolution(value);
884         return this;
885      }
886
887      @Override /* Overridden from Builder */
888      public Builder useEnumNames() {
889         super.useEnumNames();
890         return this;
891      }
892
893      @Override /* Overridden from Builder */
894      public Builder useJavaBeanIntrospector() {
895         super.useJavaBeanIntrospector();
896         return this;
897      }
898
899      /**
900       *  Use whitespace.
901       *
902       * <p>
903       * When enabled, whitespace is added to the output to improve readability.
904       *
905       * <h5 class='section'>Example:</h5>
906       * <p class='bjava'>
907       *    <jc>// Create a serializer with whitespace enabled.</jc>
908       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
909       *       .<jsm>create</jsm>()
910       *       .useWhitespace()
911       *       .build();
912       *
913       *    <jc>// A bean with a single property</jc>
914       *    <jk>public class</jk> MyBean {
915       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
916       *    }
917       *
918       *    <jc>// Produces "\{\n\t"foo": "bar"\n\}\n"</jc>
919       *    String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean());
920       * </p>
921       *
922       * @return This object.
923       */
924      public Builder useWhitespace() {
925         return useWhitespace(true);
926      }
927
928      /**
929       * Same as {@link #useWhitespace()} but allows you to explicitly specify the value.
930       *
931       * @param value The value for this setting.
932       * @return This object.
933       */
934      public Builder useWhitespace(boolean value) {
935         useWhitespace = value;
936         return this;
937      }
938
939      /**
940       *  Use whitespace.
941       *
942       * <p>
943       * When enabled, whitespace is added to the output to improve readability.
944       *
945       * <h5 class='section'>Example:</h5>
946       * <p class='bjava'>
947       *    <jc>// Create a serializer with whitespace enabled.</jc>
948       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
949       *       .<jsm>create</jsm>()
950       *       .ws()
951       *       .build();
952       *
953       *    <jc>// A bean with a single property</jc>
954       *    <jk>public class</jk> MyBean {
955       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
956       *    }
957       *
958       *    <jc>// Produces "\{\n\t"foo": "bar"\n\}\n"</jc>
959       *    String <jv>json</jv> = <jv>serializer</jv>.serialize(<jk>new</jk> MyBean());
960       * </p>
961       *
962       * @return This object.
963       */
964      public Serializer.Builder ws() {
965         return useWhitespace();
966      }
967   }
968
969   /**
970    * Creates a new builder for this object.
971    *
972    * @return A new builder.
973    */
974   public static Builder create() {
975      return new Builder();
976   }
977
978   protected final boolean useWhitespace;
979   protected final Character quoteChar;
980   protected final Character quoteCharOverride;
981   private final Charset fileCharset;
982   private final Charset streamCharset;
983   protected final int maxIndent;
984
985   private final char quoteCharValue;
986
987   /**
988    * Constructor.
989    *
990    * @param builder
991    *    The builder for this object.
992    */
993   protected WriterSerializer(Builder builder) {
994      super(builder);
995
996      fileCharset = builder.fileCharset;
997      maxIndent = builder.maxIndent;
998      quoteChar = builder.quoteChar;
999      quoteCharOverride = builder.quoteCharOverride;
1000      streamCharset = builder.streamCharset;
1001      useWhitespace = builder.useWhitespace;
1002
1003      quoteCharValue = nn(quoteCharOverride) ? quoteCharOverride : nn(quoteChar) ? quoteChar : '"';
1004   }
1005
1006   @Override /* Overridden from Context */
1007   public Builder copy() {
1008      return new Builder(this);
1009   }
1010
1011   @Override /* Overridden from Context */
1012   public WriterSerializerSession.Builder createSession() {
1013      return WriterSerializerSession.create(this);
1014   }
1015
1016   @Override /* Overridden from Context */
1017   public WriterSerializerSession getSession() { return createSession().build(); }
1018
1019   @Override /* Overridden from Serializer */
1020   public final boolean isWriterSerializer() { return true; }
1021
1022   /**
1023    * Convenience method for serializing an object and sending it to STDOUT.
1024    *
1025    * @param o The object to serialize.
1026    * @return This object.
1027    */
1028   public final WriterSerializer println(Object o) {
1029      System.out.println(toString(o));  // NOT DEBUG
1030      return this;
1031   }
1032
1033   /**
1034    * Convenience method for serializing an object to a <c>String</c>.
1035    *
1036    * @param o The object to serialize.
1037    * @return The output serialized to a string.
1038    * @throws SerializeException If a problem occurred trying to convert the output.
1039    */
1040   @Override /* Overridden from Serializer */
1041   public final String serialize(Object o) throws SerializeException {
1042      return getSession().serialize(o);
1043   }
1044
1045   /**
1046    * Identical to {@link #serialize(Object)} except throws a {@link RuntimeException} instead of a {@link SerializeException}.
1047    *
1048    * <p>
1049    * This is typically good enough for debugging purposes.
1050    *
1051    * @param o The object to serialize.
1052    * @return The serialized object.
1053    */
1054   public final String toString(Object o) {
1055      try {
1056         return serialize(o);
1057      } catch (Exception e) {
1058         throw toRex(e);
1059      }
1060   }
1061
1062   /**
1063    * File charset.
1064    *
1065    * @see Builder#fileCharset(Charset)
1066    * @return
1067    *    The character set to use when writing to <c>Files</c> on the file system.
1068    */
1069   protected final Charset getFileCharset() { return fileCharset; }
1070
1071   /**
1072    * Maximum indentation.
1073    *
1074    * @see Builder#maxIndent(int)
1075    * @return
1076    *    The maximum indentation level in the serialized document.
1077    */
1078   protected final int getMaxIndent() { return maxIndent; }
1079
1080   /**
1081    * Quote character.
1082    *
1083    * @see Builder#quoteChar(char)
1084    * @return
1085    *    The character used for quoting attributes and values.
1086    */
1087   protected char getQuoteChar() { return quoteCharValue; }
1088
1089   /**
1090    * Output stream charset.
1091    *
1092    * @see Builder#streamCharset(Charset)
1093    * @return
1094    *    The character set to use when writing to <c>OutputStreams</c> and byte arrays.
1095    */
1096   protected final Charset getStreamCharset() { return streamCharset; }
1097
1098   /**
1099    * Trim strings.
1100    *
1101    * @see Builder#useWhitespace()
1102    * @return
1103    *    When enabled, whitespace is added to the output to improve readability.
1104    */
1105   protected final boolean isUseWhitespace() { return useWhitespace; }
1106
1107   @Override /* Overridden from Serializer */
1108   protected FluentMap<String,Object> properties() {
1109      return super.properties()
1110         .a("fileCharset", fileCharset)
1111         .a("maxIndent", maxIndent)
1112         .a("quoteChar", quoteChar)
1113         .a("streamCharset", streamCharset)
1114         .a("useWhitespace", useWhitespace);
1115   }
1116
1117   /**
1118    * Quote character.
1119    *
1120    * @see Builder#quoteChar(char)
1121    * @return
1122    *    The character used for quoting attributes and values.
1123    */
1124   protected Character quoteChar() {
1125      return nn(quoteCharOverride) ? quoteCharOverride : quoteChar;
1126   }
1127}