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.uon;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import java.lang.annotation.*;
023import java.nio.charset.*;
024import java.util.*;
025import java.util.concurrent.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.commons.collections.*;
029import org.apache.juneau.commons.function.*;
030import org.apache.juneau.commons.reflect.*;
031import org.apache.juneau.httppart.*;
032import org.apache.juneau.serializer.*;
033
034/**
035 * Serializes POJO models to UON (a notation for URL-encoded query parameter values).
036 *
037 * <h5 class='topic'>Media types</h5>
038 * <p>
039 * Handles <c>Accept</c> types:  <bc>text/uon</bc>
040 * <p>
041 * Produces <c>Content-Type</c> types:  <bc>text/uon</bc>
042 *
043 * <h5 class='topic'>Description</h5>
044 * <p>
045 * This serializer provides several serialization options.
046 * Typically, one of the predefined DEFAULT serializers will be sufficient.
047 * However, custom serializers can be constructed to fine-tune behavior.
048 *
049 * <p>
050 * The following shows a sample object defined in Javascript:
051 * <p class='bjson'>
052 *    {
053 *       id: 1,
054 *       name: <js>'John Smith'</js>,
055 *       uri: <js>'http://sample/addressBook/person/1'</js>,
056 *       addressBookUri: <js>'http://sample/addressBook'</js>,
057 *       birthDate: <js>'1946-08-12T00:00:00Z'</js>,
058 *       otherIds: <jk>null</jk>,
059 *       addresses: [
060 *          {
061 *             uri: <js>'http://sample/addressBook/address/1'</js>,
062 *             personUri: <js>'http://sample/addressBook/person/1'</js>,
063 *             id: 1,
064 *             street: <js>'100 Main Street'</js>,
065 *             city: <js>'Anywhereville'</js>,
066 *             state: <js>'NY'</js>,
067 *             zip: 12345,
068 *             isCurrent: <jk>true</jk>,
069 *          }
070 *       ]
071 *    }
072 * </p>
073 *
074 * <p>
075 * Using the "strict" syntax defined in this document, the equivalent UON notation would be as follows:
076 * <p class='buon'>
077 *    (
078 *       <ua>id</ua>=<un>1</un>,
079 *       <ua>name</ua>=<us>'John+Smith'</us>,
080 *       <ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,
081 *       <ua>addressBookUri</ua>=<us>http://sample/addressBook</us>,
082 *       <ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>,
083 *       <ua>otherIds</ua>=<uk>null</uk>,
084 *       <ua>addresses</ua>=@(
085 *          (
086 *             <ua>uri</ua>=<us>http://sample/addressBook/address/1</us>,
087 *             <ua>personUri</ua>=<us>http://sample/addressBook/person/1</us>,
088 *             <ua>id</ua>=<un>1</un>,
089 *             <ua>street</ua>=<us>'100+Main+Street'</us>,
090 *             <ua>city</ua>=<us>Anywhereville</us>,
091 *             <ua>state</ua>=<us>NY</us>,
092 *             <ua>zip</ua>=<un>12345</un>,
093 *             <ua>isCurrent</ua>=<uk>true</uk>
094 *          )
095 *       )
096 *    )
097 * </p>
098 *
099 * <h5 class='section'>Example:</h5>
100 * <p class='bjava'>
101 *    <jc>// Serialize a Map</jc>
102 *    Map <jv>map</jv> = JsonMap.<jsm>ofJson</jsm>(<js>"{a:'b',c:1,d:false,e:['f',1,false],g:{h:'i'}}"</js>);
103 *
104 *    <jc>// Serialize to value equivalent to JSON.</jc>
105 *    <jc>// Produces "(a=b,c=1,d=false,e=@(f,1,false),g=(h=i))"</jc>
106 *    String <jv>uon</jv> = UonSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>map</jv>);
107 *
108 *    <jc>// Serialize a bean</jc>
109 *    <jk>public class</jk> Person {
110 *       <jk>public</jk> Person(String <jv>name</jv>);
111 *       <jk>public</jk> String getName();
112 *       <jk>public int</jk> getAge();
113 *       <jk>public</jk> Address getAddress();
114 *       <jk>public boolean</jk> deceased;
115 *    }
116 *
117 *    <jk>public class</jk> Address {
118 *       <jk>public</jk> String getStreet();
119 *       <jk>public</jk> String getCity();
120 *       <jk>public</jk> String getState();
121 *       <jk>public int</jk> getZip();
122 *    }
123 *
124 *    Person <jv>person</jv> = <jk>new</jk> Person(<js>"John Doe"</js>, 23, <js>"123 Main St"</js>, <js>"Anywhere"</js>,
125 *       <js>"NY"</js>, 12345, <jk>false</jk>);
126 *
127 *    <jc>// Produces "(name='John Doe',age=23,address=(street='123 Main St',city=Anywhere,state=NY,zip=12345),deceased=false)"</jc>
128 *    String <jv>uon</jv> = UonSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>person</jv>);
129 * </p>
130 *
131 * <h5 class='section'>Notes:</h5><ul>
132 *    <li class='note'>This class is thread safe and reusable.
133 * </ul>
134 *
135 * <h5 class='section'>See Also:</h5><ul>
136 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/UonBasics">UON Basics</a>
137
138 * </ul>
139 */
140public class UonSerializer extends WriterSerializer implements HttpPartSerializer, UonMetaProvider {
141   /**
142    * Builder class.
143    */
144   public static class Builder extends WriterSerializer.Builder {
145
146      private static final Cache<HashKey,UonSerializer> CACHE = Cache.of(HashKey.class, UonSerializer.class).build();
147
148      private boolean addBeanTypesUon;
149      private boolean encoding;
150      private ParamFormat paramFormat;
151      private Character quoteCharUon;
152
153      /**
154       * Constructor, default settings.
155       */
156      protected Builder() {
157         produces("text/uon");
158         addBeanTypesUon = env("UonSerializer.addBeanTypesUon", false);
159         encoding = env("UonSerializer.encoding", false);
160         paramFormat = env("UonSerializer.paramFormat", ParamFormat.UON);
161         quoteCharUon = null;
162      }
163
164      /**
165       * Copy constructor.
166       *
167       * @param copyFrom The builder to copy from.
168       *    <br>Cannot be <jk>null</jk>.
169       */
170      protected Builder(Builder copyFrom) {
171         super(assertArgNotNull("copyFrom", copyFrom));
172         addBeanTypesUon = copyFrom.addBeanTypesUon;
173         encoding = copyFrom.encoding;
174         paramFormat = copyFrom.paramFormat;
175         quoteCharUon = copyFrom.quoteCharUon;
176      }
177
178      /**
179       * Copy constructor.
180       *
181       * @param copyFrom The bean to copy from.
182       *    <br>Cannot be <jk>null</jk>.
183       */
184      protected Builder(UonSerializer copyFrom) {
185         super(assertArgNotNull("copyFrom", copyFrom));
186         addBeanTypesUon = copyFrom.addBeanTypesUon;
187         encoding = copyFrom.encoding;
188         paramFormat = copyFrom.paramFormat;
189         quoteCharUon = copyFrom.quoteCharUon;
190      }
191
192      @Override /* Overridden from Builder */
193      public Builder accept(String value) {
194         super.accept(value);
195         return this;
196      }
197
198      @Override /* Overridden from Builder */
199      public Builder addBeanTypes() {
200         super.addBeanTypes();
201         return this;
202      }
203
204      @Override /* Overridden from Builder */
205      public Builder addBeanTypes(boolean value) {
206         super.addBeanTypes(value);
207         return this;
208      }
209
210      /**
211       * Add <js>"_type"</js> properties when needed.
212       *
213       * <p>
214       * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
215       * through reflection.
216       *
217       * <p>
218       * When present, this value overrides the {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} setting and is
219       * provided to customize the behavior of specific serializers in a {@link SerializerSet}.
220       *
221       * @return This object.
222       */
223      public Builder addBeanTypesUon() {
224         return addBeanTypesUon(true);
225      }
226
227      /**
228       * Same as {@link #addBeanTypesUon()} but allows you to explicitly specify the value.
229       *
230       * @param value The value for this setting.
231       * @return This object.
232       */
233      public Builder addBeanTypesUon(boolean value) {
234         addBeanTypesUon = value;
235         return this;
236      }
237
238      @Override /* Overridden from Builder */
239      public Builder addRootType() {
240         super.addRootType();
241         return this;
242      }
243
244      @Override /* Overridden from Builder */
245      public Builder addRootType(boolean value) {
246         super.addRootType(value);
247         return this;
248      }
249
250      @Override /* Overridden from Builder */
251      public Builder annotations(Annotation...values) {
252         super.annotations(values);
253         return this;
254      }
255
256      @Override /* Overridden from Builder */
257      public Builder apply(AnnotationWorkList work) {
258         super.apply(work);
259         return this;
260      }
261
262      @Override /* Overridden from Builder */
263      public Builder applyAnnotations(Class<?>...from) {
264         super.applyAnnotations(from);
265         return this;
266      }
267
268      @Override /* Overridden from Builder */
269      public Builder applyAnnotations(Object...from) {
270         super.applyAnnotations(from);
271         return this;
272      }
273
274      @Override /* Overridden from Builder */
275      public Builder beanClassVisibility(Visibility value) {
276         super.beanClassVisibility(value);
277         return this;
278      }
279
280      @Override /* Overridden from Builder */
281      public Builder beanConstructorVisibility(Visibility value) {
282         super.beanConstructorVisibility(value);
283         return this;
284      }
285
286      @Override /* Overridden from Builder */
287      public Builder beanContext(BeanContext value) {
288         super.beanContext(value);
289         return this;
290      }
291
292      @Override /* Overridden from Builder */
293      public Builder beanContext(BeanContext.Builder value) {
294         super.beanContext(value);
295         return this;
296      }
297
298      @Override /* Overridden from Builder */
299      public Builder beanDictionary(java.lang.Class<?>...values) {
300         super.beanDictionary(values);
301         return this;
302      }
303
304      @Override /* Overridden from Builder */
305      public Builder beanFieldVisibility(Visibility value) {
306         super.beanFieldVisibility(value);
307         return this;
308      }
309
310      @Override /* Overridden from Builder */
311      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
312         super.beanInterceptor(on, value);
313         return this;
314      }
315
316      @Override /* Overridden from Builder */
317      public Builder beanMapPutReturnsOldValue() {
318         super.beanMapPutReturnsOldValue();
319         return this;
320      }
321
322      @Override /* Overridden from Builder */
323      public Builder beanMethodVisibility(Visibility value) {
324         super.beanMethodVisibility(value);
325         return this;
326      }
327
328      @Override /* Overridden from Builder */
329      public Builder beanProperties(Class<?> beanClass, String properties) {
330         super.beanProperties(beanClass, properties);
331         return this;
332      }
333
334      @Override /* Overridden from Builder */
335      public Builder beanProperties(Map<String,Object> values) {
336         super.beanProperties(values);
337         return this;
338      }
339
340      @Override /* Overridden from Builder */
341      public Builder beanProperties(String beanClassName, String properties) {
342         super.beanProperties(beanClassName, properties);
343         return this;
344      }
345
346      @Override /* Overridden from Builder */
347      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
348         super.beanPropertiesExcludes(beanClass, properties);
349         return this;
350      }
351
352      @Override /* Overridden from Builder */
353      public Builder beanPropertiesExcludes(Map<String,Object> values) {
354         super.beanPropertiesExcludes(values);
355         return this;
356      }
357
358      @Override /* Overridden from Builder */
359      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
360         super.beanPropertiesExcludes(beanClassName, properties);
361         return this;
362      }
363
364      @Override /* Overridden from Builder */
365      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
366         super.beanPropertiesReadOnly(beanClass, properties);
367         return this;
368      }
369
370      @Override /* Overridden from Builder */
371      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
372         super.beanPropertiesReadOnly(values);
373         return this;
374      }
375
376      @Override /* Overridden from Builder */
377      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
378         super.beanPropertiesReadOnly(beanClassName, properties);
379         return this;
380      }
381
382      @Override /* Overridden from Builder */
383      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
384         super.beanPropertiesWriteOnly(beanClass, properties);
385         return this;
386      }
387
388      @Override /* Overridden from Builder */
389      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
390         super.beanPropertiesWriteOnly(values);
391         return this;
392      }
393
394      @Override /* Overridden from Builder */
395      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
396         super.beanPropertiesWriteOnly(beanClassName, properties);
397         return this;
398      }
399
400      @Override /* Overridden from Builder */
401      public Builder beansRequireDefaultConstructor() {
402         super.beansRequireDefaultConstructor();
403         return this;
404      }
405
406      @Override /* Overridden from Builder */
407      public Builder beansRequireSerializable() {
408         super.beansRequireSerializable();
409         return this;
410      }
411
412      @Override /* Overridden from Builder */
413      public Builder beansRequireSettersForGetters() {
414         super.beansRequireSettersForGetters();
415         return this;
416      }
417
418      @Override /* Overridden from Context.Builder */
419      public UonSerializer build() {
420         return cache(CACHE).build(UonSerializer.class);
421      }
422
423      @Override /* Overridden from Builder */
424      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
425         super.cache(value);
426         return this;
427      }
428
429      @Override /* Overridden from Context.Builder */
430      public Builder copy() {
431         return new Builder(this);
432      }
433
434      @Override /* Overridden from Builder */
435      public Builder debug() {
436         super.debug();
437         return this;
438      }
439
440      @Override /* Overridden from Builder */
441      public Builder debug(boolean value) {
442         super.debug(value);
443         return this;
444      }
445
446      @Override /* Overridden from Builder */
447      public Builder detectRecursions() {
448         super.detectRecursions();
449         return this;
450      }
451
452      @Override /* Overridden from Builder */
453      public Builder detectRecursions(boolean value) {
454         super.detectRecursions(value);
455         return this;
456      }
457
458      @Override /* Overridden from Builder */
459      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
460         super.dictionaryOn(on, values);
461         return this;
462      }
463
464      @Override /* Overridden from Builder */
465      public Builder disableBeansRequireSomeProperties() {
466         super.disableBeansRequireSomeProperties();
467         return this;
468      }
469
470      @Override /* Overridden from Builder */
471      public Builder disableIgnoreMissingSetters() {
472         super.disableIgnoreMissingSetters();
473         return this;
474      }
475
476      @Override /* Overridden from Builder */
477      public Builder disableIgnoreTransientFields() {
478         super.disableIgnoreTransientFields();
479         return this;
480      }
481
482      @Override /* Overridden from Builder */
483      public Builder disableIgnoreUnknownNullBeanProperties() {
484         super.disableIgnoreUnknownNullBeanProperties();
485         return this;
486      }
487
488      @Override /* Overridden from Builder */
489      public Builder disableInterfaceProxies() {
490         super.disableInterfaceProxies();
491         return this;
492      }
493
494      /**
495       * Encode non-valid URI characters.
496       *
497       * <p>
498       * Encode non-valid URI characters with <js>"%xx"</js> constructs.
499       *
500       * <p>
501       * If <jk>true</jk>, non-valid URI characters will be converted to <js>"%xx"</js> sequences.
502       * <br>Set to <jk>false</jk> if parameter value is being passed to some other code that will already perform
503       * URL-encoding of non-valid URI characters.
504       *
505       * <h5 class='section'>Example:</h5>
506       * <p class='bjava'>
507       *    <jc>// Create a non-encoding UON serializer.</jc>
508       *    UonSerializer <jv>serializer1</jv> = UonSerializer.
509       *       .<jsm>create</jsm>()
510       *       .build();
511       *
512       *    <jc>// Create an encoding UON serializer.</jc>
513       *    UonSerializer <jv>serializer2</jv> = UonSerializer.
514       *       .<jsm>create</jsm>()
515       *       .encoding()
516       *       .build();
517       *
518       *    JsonMap <jv>map</jv> = JsonMap.<jsm>of</jsm>(<js>"foo"</js>, <js>"foo bar"</js>);
519       *
520       *    <jc>// Produces: "(foo=foo bar)"</jc>
521       *    String <jv>uon1</jv> = <jv>serializer1</jv>.serialize(<jv>map</jv>)
522       *
523       *    <jc>// Produces: "(foo=foo%20bar)"</jc>
524       *    String <jv>uon2</jv> = <jv>serializer2</jv>.serialize(<jv>map</jv>)
525       * </p>
526       *
527       * @return This object.
528       */
529      public Builder encoding() {
530         return encoding(true);
531      }
532
533      /**
534       * Same as {@link #encoding()} but allows you to disable the previous setting.
535       *
536       * @param value The new value for this setting.
537       * @return This object.
538       */
539      public Builder encoding(boolean value) {
540         encoding = value;
541         return this;
542      }
543
544      @Override /* Overridden from Builder */
545      public <T> Builder example(Class<T> pojoClass, String json) {
546         super.example(pojoClass, json);
547         return this;
548      }
549
550      @Override /* Overridden from Builder */
551      public <T> Builder example(Class<T> pojoClass, T o) {
552         super.example(pojoClass, o);
553         return this;
554      }
555
556      @Override /* Overridden from Builder */
557      public Builder fileCharset(Charset value) {
558         super.fileCharset(value);
559         return this;
560      }
561
562      @Override /* Overridden from Builder */
563      public Builder findFluentSetters() {
564         super.findFluentSetters();
565         return this;
566      }
567
568      @Override /* Overridden from Builder */
569      public Builder findFluentSetters(Class<?> on) {
570         super.findFluentSetters(on);
571         return this;
572      }
573
574      @Override /* Overridden from Context.Builder */
575      public HashKey hashKey() {
576         // @formatter:off
577         return HashKey.of(
578            super.hashKey(),
579            addBeanTypesUon,
580            encoding,
581            paramFormat,
582            quoteCharUon
583         );
584         // @formatter:on
585      }
586
587      @Override /* Overridden from Builder */
588      public Builder ignoreInvocationExceptionsOnGetters() {
589         super.ignoreInvocationExceptionsOnGetters();
590         return this;
591      }
592
593      @Override /* Overridden from Builder */
594      public Builder ignoreInvocationExceptionsOnSetters() {
595         super.ignoreInvocationExceptionsOnSetters();
596         return this;
597      }
598
599      @Override /* Overridden from Builder */
600      public Builder ignoreRecursions() {
601         super.ignoreRecursions();
602         return this;
603      }
604
605      @Override /* Overridden from Builder */
606      public Builder ignoreRecursions(boolean value) {
607         super.ignoreRecursions(value);
608         return this;
609      }
610
611      @Override /* Overridden from Builder */
612      public Builder ignoreUnknownBeanProperties() {
613         super.ignoreUnknownBeanProperties();
614         return this;
615      }
616
617      @Override /* Overridden from Builder */
618      public Builder ignoreUnknownEnumValues() {
619         super.ignoreUnknownEnumValues();
620         return this;
621      }
622
623      @Override /* Overridden from Builder */
624      public Builder impl(Context value) {
625         super.impl(value);
626         return this;
627      }
628
629      @Override /* Overridden from Builder */
630      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
631         super.implClass(interfaceClass, implClass);
632         return this;
633      }
634
635      @Override /* Overridden from Builder */
636      public Builder implClasses(Map<Class<?>,Class<?>> values) {
637         super.implClasses(values);
638         return this;
639      }
640
641      @Override /* Overridden from Builder */
642      public Builder initialDepth(int value) {
643         super.initialDepth(value);
644         return this;
645      }
646
647      @Override /* Overridden from Builder */
648      public Builder interfaceClass(Class<?> on, Class<?> value) {
649         super.interfaceClass(on, value);
650         return this;
651      }
652
653      @Override /* Overridden from Builder */
654      public Builder interfaces(java.lang.Class<?>...value) {
655         super.interfaces(value);
656         return this;
657      }
658
659      @Override /* Overridden from Builder */
660      public Builder keepNullProperties() {
661         super.keepNullProperties();
662         return this;
663      }
664
665      @Override /* Overridden from Builder */
666      public Builder keepNullProperties(boolean value) {
667         super.keepNullProperties(value);
668         return this;
669      }
670
671      @Override /* Overridden from Builder */
672      public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) {
673         super.listener(value);
674         return this;
675      }
676
677      @Override /* Overridden from Builder */
678      public Builder locale(Locale value) {
679         super.locale(value);
680         return this;
681      }
682
683      @Override /* Overridden from Builder */
684      public Builder maxDepth(int value) {
685         super.maxDepth(value);
686         return this;
687      }
688
689      @Override /* Overridden from Builder */
690      public Builder maxIndent(int value) {
691         super.maxIndent(value);
692         return this;
693      }
694
695      @Override /* Overridden from Builder */
696      public Builder mediaType(MediaType value) {
697         super.mediaType(value);
698         return this;
699      }
700
701      @Override /* Overridden from Builder */
702      public Builder notBeanClasses(java.lang.Class<?>...values) {
703         super.notBeanClasses(values);
704         return this;
705      }
706
707      @Override /* Overridden from Builder */
708      public Builder notBeanPackages(String...values) {
709         super.notBeanPackages(values);
710         return this;
711      }
712
713      /**
714       * Format to use for query/form-data/header values.
715       *
716       * <p>
717       * Specifies the format to use for URL GET parameter keys and values.
718       *
719       * <h5 class='section'>Example:</h5>
720       * <p class='bjava'>
721       *    <jc>// Create a normal UON serializer.</jc>
722       *    UonSerializer <jv>serializer1</jv> = UonSerializer
723       *       .<jsm>create</jsm>()
724       *       .build();
725       *
726       *    <jc>// Create a plain-text UON serializer.</jc>
727       *    UonSerializer <jv>serializer2</jv> = UonSerializer
728       *       .<jsm>create</jsm>()
729       *       .paramFormat(<jsf>PLAIN_TEXT</jsf>)
730       *       .build();
731       *
732       *    JsonMap <jv>map</jv> = JsonMap.<jsm>of</jsm>(
733       *       <js>"foo"</js>, <js>"bar"</js>,
734       *       <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>}
735       *    );
736       *
737       *    <jc>// Produces: "(foo=bar,baz=@(qux,'true','123'))"</jc>
738       *    String <jv>uon1</jv> = <jv>serializer1</jv>.serialize(<jv>map</jv>)
739       *
740       *    <jc>// Produces: "foo=bar,baz=qux,true,123"</jc>
741       *    String <jv>uon2</jv> = <jv>serializer2</jv>.serialize(<jv>map</jv>)
742       * </p>
743       *
744       * <p>
745       * <ul class='values javatree'>
746       *    <li class='jf'>{@link ParamFormat#UON} (default) - Use UON notation for parameters.
747       *    <li class='jf'>{@link ParamFormat#PLAINTEXT} - Use plain text for parameters.
748       * </ul>
749       *
750       * @param value
751       *    The new value for this property.
752       *    <br>The default value is {@link ParamFormat#UON}.
753       *    <br>Cannot be <jk>null</jk>.
754       * @return This object.
755       */
756      public Builder paramFormat(ParamFormat value) {
757         paramFormat = assertArgNotNull("value", value);
758         return this;
759      }
760
761      /**
762       * Format to use for query/form-data/header values.
763       *
764       * <p>
765       * Specifies plain-text for the format to use for URL GET parameter keys and values.
766       *
767       * <h5 class='section'>Example:</h5>
768       * <p class='bjava'>
769       *    <jc>// Create a plain-text UON serializer.</jc>
770       *    UonSerializer <jv>serializer</jv> = UonSerializer
771       *       .<jsm>create</jsm>()
772       *       .paramFormatPlain()
773       *       .build();
774       *
775       *    JsonMap <jv>map</jv> = JsonMap.<jsm>of</jsm>(
776       *       <js>"foo"</js>, <js>"bar"</js>,
777       *       <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>}
778       *    );
779       *
780       *    <jc>// Produces: "foo=bar,baz=qux,true,123"</jc>
781       *    String <jv>uon</jv> = <jv>serializer</jv>.serialize(<jv>map</jv>)
782       * </p>
783       *
784       * @return This object.
785       */
786      public Builder paramFormatPlain() {
787         return paramFormat(ParamFormat.PLAINTEXT);
788      }
789
790      @Override /* Overridden from Builder */
791      public Builder produces(String value) {
792         super.produces(value);
793         return this;
794      }
795
796      @Override /* Overridden from Builder */
797      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
798         super.propertyNamer(on, value);
799         return this;
800      }
801
802      @Override /* Overridden from Builder */
803      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
804         super.propertyNamer(value);
805         return this;
806      }
807
808      @Override /* Overridden from Builder */
809      public Builder quoteChar(char value) {
810         super.quoteChar(value);
811         return this;
812      }
813
814      @Override /* Overridden from Builder */
815      public Builder quoteCharOverride(char value) {
816         super.quoteCharOverride(value);
817         return this;
818      }
819
820      /**
821       * Specifies the quote character.
822       *
823       * @param value The value for this setting.
824       * @return This object.
825       */
826      public Builder quoteCharUon(char value) {
827         quoteCharUon = value;
828         return this;
829      }
830
831      @Override /* Overridden from Builder */
832      public Builder sortCollections() {
833         super.sortCollections();
834         return this;
835      }
836
837      @Override /* Overridden from Builder */
838      public Builder sortCollections(boolean value) {
839         super.sortCollections(value);
840         return this;
841      }
842
843      @Override /* Overridden from Builder */
844      public Builder sortMaps() {
845         super.sortMaps();
846         return this;
847      }
848
849      @Override /* Overridden from Builder */
850      public Builder sortMaps(boolean value) {
851         super.sortMaps(value);
852         return this;
853      }
854
855      @Override /* Overridden from Builder */
856      public Builder sortProperties() {
857         super.sortProperties();
858         return this;
859      }
860
861      @Override /* Overridden from Builder */
862      public Builder sortProperties(java.lang.Class<?>...on) {
863         super.sortProperties(on);
864         return this;
865      }
866
867      @Override /* Overridden from Builder */
868      public Builder sq() {
869         super.sq();
870         return this;
871      }
872
873      @Override /* Overridden from Builder */
874      public Builder stopClass(Class<?> on, Class<?> value) {
875         super.stopClass(on, value);
876         return this;
877      }
878
879      @Override /* Overridden from Builder */
880      public Builder streamCharset(Charset value) {
881         super.streamCharset(value);
882         return this;
883      }
884
885      @Override /* Overridden from Builder */
886      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
887         super.swap(normalClass, swappedClass, swapFunction);
888         return this;
889      }
890
891      @Override /* Overridden from Builder */
892      public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
893         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
894         return this;
895      }
896
897      @Override /* Overridden from Builder */
898      public Builder swaps(Class<?>...values) {
899         super.swaps(values);
900         return this;
901      }
902
903      @Override /* Overridden from Builder */
904      public Builder swaps(Object...values) {
905         super.swaps(values);
906         return this;
907      }
908
909      @Override /* Overridden from Builder */
910      public Builder timeZone(TimeZone value) {
911         super.timeZone(value);
912         return this;
913      }
914
915      @Override /* Overridden from Builder */
916      public Builder trimEmptyCollections() {
917         super.trimEmptyCollections();
918         return this;
919      }
920
921      @Override /* Overridden from Builder */
922      public Builder trimEmptyCollections(boolean value) {
923         super.trimEmptyCollections(value);
924         return this;
925      }
926
927      @Override /* Overridden from Builder */
928      public Builder trimEmptyMaps() {
929         super.trimEmptyMaps();
930         return this;
931      }
932
933      @Override /* Overridden from Builder */
934      public Builder trimEmptyMaps(boolean value) {
935         super.trimEmptyMaps(value);
936         return this;
937      }
938
939      @Override /* Overridden from Builder */
940      public Builder trimStrings() {
941         super.trimStrings();
942         return this;
943      }
944
945      @Override /* Overridden from Builder */
946      public Builder trimStrings(boolean value) {
947         super.trimStrings(value);
948         return this;
949      }
950
951      @Override /* Overridden from Builder */
952      public Builder type(Class<? extends org.apache.juneau.Context> value) {
953         super.type(value);
954         return this;
955      }
956
957      @Override /* Overridden from Builder */
958      public Builder typeName(Class<?> on, String value) {
959         super.typeName(on, value);
960         return this;
961      }
962
963      @Override /* Overridden from Builder */
964      public Builder typePropertyName(Class<?> on, String value) {
965         super.typePropertyName(on, value);
966         return this;
967      }
968
969      @Override /* Overridden from Builder */
970      public Builder typePropertyName(String value) {
971         super.typePropertyName(value);
972         return this;
973      }
974
975      @Override /* Overridden from Builder */
976      public Builder uriContext(UriContext value) {
977         super.uriContext(value);
978         return this;
979      }
980
981      @Override /* Overridden from Builder */
982      public Builder uriRelativity(UriRelativity value) {
983         super.uriRelativity(value);
984         return this;
985      }
986
987      @Override /* Overridden from Builder */
988      public Builder uriResolution(UriResolution value) {
989         super.uriResolution(value);
990         return this;
991      }
992
993      @Override /* Overridden from Builder */
994      public Builder useEnumNames() {
995         super.useEnumNames();
996         return this;
997      }
998
999      @Override /* Overridden from Builder */
1000      public Builder useJavaBeanIntrospector() {
1001         super.useJavaBeanIntrospector();
1002         return this;
1003      }
1004
1005      @Override /* Overridden from Builder */
1006      public Builder useWhitespace() {
1007         super.useWhitespace();
1008         return this;
1009      }
1010
1011      @Override /* Overridden from Builder */
1012      public Builder useWhitespace(boolean value) {
1013         super.useWhitespace(value);
1014         return this;
1015      }
1016
1017      @Override /* Overridden from Builder */
1018      public Builder ws() {
1019         super.ws();
1020         return this;
1021      }
1022   }
1023
1024   /**
1025    * Equivalent to <code>UonSerializer.<jsm>create</jsm>().encoding().build();</code>.
1026    */
1027   public static class Encoding extends UonSerializer {
1028
1029      /**
1030       * Constructor.
1031       *
1032       * @param builder The builder for this object.
1033       */
1034      public Encoding(Builder builder) {
1035         super(builder.encoding());
1036      }
1037   }
1038
1039   /**
1040    * Equivalent to <code>UonSerializer.<jsm>create</jsm>().ws().build();</code>.
1041    */
1042   public static class Readable extends UonSerializer {
1043
1044      /**
1045       * Constructor.
1046       *
1047       * @param builder The builder for this object.
1048       */
1049      public Readable(Builder builder) {
1050         super(builder.useWhitespace());
1051      }
1052   }
1053
1054   /** Reusable instance of {@link UonSerializer}, all default settings. */
1055   public static final UonSerializer DEFAULT = new UonSerializer(create());
1056   /** Reusable instance of {@link UonSerializer.Readable}. */
1057   public static final UonSerializer DEFAULT_READABLE = new Readable(create());
1058
1059   /** Reusable instance of {@link UonSerializer.Encoding}. */
1060   public static final UonSerializer DEFAULT_ENCODING = new Encoding(create());
1061
1062   /**
1063    * Creates a new builder for this object.
1064    *
1065    * @return A new builder.
1066    */
1067   public static Builder create() {
1068      return new Builder();
1069   }
1070
1071   protected final boolean addBeanTypesUon;
1072   protected final boolean encoding;
1073   protected final Character quoteCharUon;
1074   protected final ParamFormat paramFormat;
1075   private final boolean addBeanTypes;
1076
1077   private final char quoteChar;
1078   private final Map<BeanPropertyMeta,UonBeanPropertyMeta> uonBeanPropertyMetas = new ConcurrentHashMap<>();
1079   private final Map<ClassMeta<?>,UonClassMeta> uonClassMetas = new ConcurrentHashMap<>();
1080
1081   /**
1082    * Constructor.
1083    *
1084    * @param builder
1085    *    The builder for this object.
1086    */
1087   public UonSerializer(Builder builder) {
1088      super(builder);
1089
1090      addBeanTypesUon = builder.addBeanTypesUon;
1091      encoding = builder.encoding;
1092      paramFormat = builder.paramFormat;
1093      quoteCharUon = builder.quoteCharUon;
1094
1095      addBeanTypes = addBeanTypesUon || super.isAddBeanTypes();
1096      quoteChar = nn(quoteCharUon) ? quoteCharUon : nn(super.quoteChar()) ? super.quoteChar() : '\'';
1097   }
1098
1099   @Override /* Overridden from Context */
1100   public Builder copy() {
1101      return new Builder(this);
1102   }
1103
1104   @Override /* Overridden from Context */
1105   public UonSerializerSession.Builder createSession() {
1106      return UonSerializerSession.create(this);
1107   }
1108
1109   @Override /* Overridden from HttpPartSerializer */
1110   public UonSerializerSession getPartSession() { return UonSerializerSession.create(this).build(); }
1111
1112   @Override /* Overridden from Context */
1113   public UonSerializerSession getSession() { return createSession().build(); }
1114
1115   @Override /* Overridden from UonMetaProvider */
1116   public UonBeanPropertyMeta getUonBeanPropertyMeta(BeanPropertyMeta bpm) {
1117      if (bpm == null)
1118         return UonBeanPropertyMeta.DEFAULT;
1119      UonBeanPropertyMeta m = uonBeanPropertyMetas.get(bpm);
1120      if (m == null) {
1121         m = new UonBeanPropertyMeta(bpm.getDelegateFor(), this);
1122         uonBeanPropertyMetas.put(bpm, m);
1123      }
1124      return m;
1125   }
1126
1127   @Override /* Overridden from UonMetaProvider */
1128   public UonClassMeta getUonClassMeta(ClassMeta<?> cm) {
1129      UonClassMeta m = uonClassMetas.get(cm);
1130      if (m == null) {
1131         m = new UonClassMeta(cm, this);
1132         uonClassMetas.put(cm, m);
1133      }
1134      return m;
1135   }
1136
1137   /**
1138    * Converts the specified value to a string that can be used as an HTTP header value, query parameter value,
1139    * form-data parameter, or URI path variable.
1140    *
1141    * <p>
1142    * Returned values should NOT be URL-encoded.
1143    *
1144    * @param partType The category of value being serialized.
1145    * @param schema
1146    *    Schema information about the part.
1147    *    <br>May be <jk>null</jk>.
1148    *    <br>Not all part serializers use the schema information.
1149    * @param value The value being serialized.
1150    * @return The serialized value.
1151    * @throws SerializeException If a problem occurred while trying to parse the input.
1152    * @throws SchemaValidationException If the output fails schema validation.
1153    */
1154   public String serialize(HttpPartType partType, HttpPartSchema schema, Object value) throws SchemaValidationException, SerializeException {
1155      return getPartSession().serialize(partType, schema, value);
1156   }
1157
1158   /**
1159    * Format to use for query/form-data/header values.
1160    *
1161    * @see Builder#paramFormat(ParamFormat)
1162    * @return
1163    *    Specifies the format to use for URL GET parameter keys and values.
1164    */
1165   protected final ParamFormat getParamFormat() { return paramFormat; }
1166
1167   /**
1168    * Quote character.
1169    *
1170    * @see Builder#quoteCharUon(char)
1171    * @return
1172    *    The character used for quoting attributes and values.
1173    */
1174   @Override
1175   protected final char getQuoteChar() { return quoteChar; }
1176
1177   /**
1178    * Add <js>"_type"</js> properties when needed.
1179    *
1180    * @see Builder#addBeanTypesUon()
1181    * @return
1182    *    <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred
1183    *    through reflection.
1184    */
1185   @Override
1186   protected final boolean isAddBeanTypes() { return addBeanTypes; }
1187
1188   /**
1189    * Encode non-valid URI characters.
1190    *
1191    * @see Builder#encoding()
1192    * @return
1193    *    <jk>true</jk> if non-valid URI characters should be encoded with <js>"%xx"</js> constructs.
1194    */
1195   protected final boolean isEncoding() { return encoding; }
1196
1197   @Override /* Overridden from WriterSerializer */
1198   protected FluentMap<String,Object> properties() {
1199      return super.properties()
1200         .a("addBeanTypes", addBeanTypes)
1201         .a("encoding", encoding)
1202         .a("paramFormat", paramFormat);
1203   }
1204}