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