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