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