001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.urlencoding;
014
015import static org.apache.juneau.collections.JsonMap.*;
016import java.lang.annotation.*;
017import java.lang.reflect.*;
018import java.nio.charset.*;
019import java.util.*;
020import java.util.concurrent.*;
021
022import org.apache.juneau.*;
023import org.apache.juneau.collections.*;
024import org.apache.juneau.internal.*;
025import org.apache.juneau.uon.*;
026import org.apache.juneau.utils.*;
027
028/**
029 * Parses URL-encoded text into POJO models.
030 *
031 * <h5 class='topic'>Media types</h5>
032 * <p>
033 * Handles <c>Content-Type</c> types:  <bc>application/x-www-form-urlencoded</bc>
034 *
035 * <h5 class='topic'>Description</h5>
036 * <p>
037 * Parses URL-Encoded text (e.g. <js>"foo=bar&amp;baz=bing"</js>) into POJOs.
038 *
039 * <p>
040 * Expects parameter values to be in UON notation.
041 *
042 * <p>
043 * This parser uses a state machine, which makes it very fast and efficient.
044 *
045 * <h5 class='section'>Notes:</h5><ul>
046 *    <li class='note'>This class is thread safe and reusable.
047 * </ul>
048 *
049 * <h5 class='section'>See Also:</h5><ul>
050 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.UrlEncodingDetails">URL-Encoding Details</a>
051 * </ul>
052 */
053public class UrlEncodingParser extends UonParser implements UrlEncodingMetaProvider {
054
055   //-------------------------------------------------------------------------------------------------------------------
056   // Static
057   //-------------------------------------------------------------------------------------------------------------------
058
059   /** Reusable instance of {@link UrlEncodingParser}. */
060   public static final UrlEncodingParser DEFAULT = new UrlEncodingParser(create());
061
062   /**
063    * Creates a new builder for this object.
064    *
065    * @return A new builder.
066    */
067   public static Builder create() {
068      return new Builder();
069   }
070
071   //-------------------------------------------------------------------------------------------------------------------
072   // Builder
073   //-------------------------------------------------------------------------------------------------------------------
074
075   /**
076    * Builder class.
077    */
078   @FluentSetters
079   public static class Builder extends UonParser.Builder {
080
081      private static final Cache<HashKey,UrlEncodingParser> CACHE = Cache.of(HashKey.class, UrlEncodingParser.class).build();
082
083      boolean expandedParams;
084
085      /**
086       * Constructor, default settings.
087       */
088      protected Builder() {
089         super();
090         decoding();
091         consumes("application/x-www-form-urlencoded");
092         expandedParams = env("UrlEncoding.expandedParams", false);
093      }
094
095      /**
096       * Copy constructor.
097       *
098       * @param copyFrom The bean to copy from.
099       */
100      protected Builder(UrlEncodingParser copyFrom) {
101         super(copyFrom);
102         expandedParams = copyFrom.expandedParams;
103      }
104
105      /**
106       * Copy constructor.
107       *
108       * @param copyFrom The builder to copy from.
109       */
110      protected Builder(Builder copyFrom) {
111         super(copyFrom);
112         expandedParams = copyFrom.expandedParams;
113      }
114
115      @Override /* Context.Builder */
116      public Builder copy() {
117         return new Builder(this);
118      }
119
120      @Override /* Context.Builder */
121      public UrlEncodingParser build() {
122         return cache(CACHE).build(UrlEncodingParser.class);
123      }
124
125      @Override /* Context.Builder */
126      public HashKey hashKey() {
127         return HashKey.of(
128            super.hashKey(),
129            expandedParams
130         );
131      }
132
133      //-----------------------------------------------------------------------------------------------------------------
134      // Properties
135      //-----------------------------------------------------------------------------------------------------------------
136
137      /**
138       * Serialize bean property collections/arrays as separate key/value pairs.
139       *
140       * <p>
141       * This is the parser-side equivalent of the {@link UrlEncodingSerializer.Builder#expandedParams()} setting.
142       *
143       * <p>
144       * If <jk>false</jk>, serializing the array <c>[1,2,3]</c> results in <c>?key=$a(1,2,3)</c>.
145       * <br>If <jk>true</jk>, serializing the same array results in <c>?key=1&amp;key=2&amp;key=3</c>.
146       *
147       * <h5 class='section'>Example:</h5>
148       * <p class='bjava'>
149       *    <jk>public class</jk> MyBean {
150       *       <jk>public</jk> String[] <jf>f1</jf>;
151       *       <jk>public</jk> List&lt;String&gt; <jf>f2</jf>;
152       *    }
153       *
154       *    UrlEncodingParser <jv>parser1</jv> = UrlEncodingParser.<jsf>DEFAULT</jsf>;
155       *    UrlEncodingParser <jv>parser2</jv> = UrlEncodingParser.<jsm>create</jsm>().expandedParams().build();
156       *
157       *    MyBean <jv>myBean1</jv> = <jv>parser1</jv>.parse(<js>"f1=@(a,b)&amp;f2=@(c,d)"</js>, A.<jk>class</jk>);
158       *
159       *    MyBean <jv>myBean2</jv> = <jv>parser2</jv>.parse(<js>"f1=a&amp;f1=b&amp;f2=c&amp;f2=d"</js>, A.<jk>class</jk>);
160       * </p>
161       *
162       * <p>
163       * This option only applies to beans.
164       *
165       * <h5 class='section'>Notes:</h5><ul>
166       *    <li class='note'>
167       *       If parsing multi-part parameters, it's highly recommended to use Collections or Lists
168       *       as bean property types instead of arrays since arrays have to be recreated from scratch every time a value
169       *       is added to it.
170       * </ul>
171       *
172       * @return This object.
173       */
174      @FluentSetter
175      public Builder expandedParams() {
176         return expandedParams(true);
177      }
178
179      /**
180       * Same as {@link #expandedParams()} but allows you to explicitly specify the value.
181       *
182       * @param value The value for this setting.
183       * @return This object.
184       */
185      @FluentSetter
186      public Builder expandedParams(boolean value) {
187         expandedParams = value;
188         return this;
189      }
190
191      // <FluentSetters>
192
193      @Override /* GENERATED - org.apache.juneau.Context.Builder */
194      public Builder annotations(Annotation...values) {
195         super.annotations(values);
196         return this;
197      }
198
199      @Override /* GENERATED - org.apache.juneau.Context.Builder */
200      public Builder apply(AnnotationWorkList work) {
201         super.apply(work);
202         return this;
203      }
204
205      @Override /* GENERATED - org.apache.juneau.Context.Builder */
206      public Builder applyAnnotations(java.lang.Class<?>...fromClasses) {
207         super.applyAnnotations(fromClasses);
208         return this;
209      }
210
211      @Override /* GENERATED - org.apache.juneau.Context.Builder */
212      public Builder applyAnnotations(Method...fromMethods) {
213         super.applyAnnotations(fromMethods);
214         return this;
215      }
216
217      @Override /* GENERATED - org.apache.juneau.Context.Builder */
218      public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) {
219         super.cache(value);
220         return this;
221      }
222
223      @Override /* GENERATED - org.apache.juneau.Context.Builder */
224      public Builder debug() {
225         super.debug();
226         return this;
227      }
228
229      @Override /* GENERATED - org.apache.juneau.Context.Builder */
230      public Builder debug(boolean value) {
231         super.debug(value);
232         return this;
233      }
234
235      @Override /* GENERATED - org.apache.juneau.Context.Builder */
236      public Builder impl(Context value) {
237         super.impl(value);
238         return this;
239      }
240
241      @Override /* GENERATED - org.apache.juneau.Context.Builder */
242      public Builder type(Class<? extends org.apache.juneau.Context> value) {
243         super.type(value);
244         return this;
245      }
246
247      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
248      public Builder beanClassVisibility(Visibility value) {
249         super.beanClassVisibility(value);
250         return this;
251      }
252
253      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
254      public Builder beanConstructorVisibility(Visibility value) {
255         super.beanConstructorVisibility(value);
256         return this;
257      }
258
259      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
260      public Builder beanContext(BeanContext value) {
261         super.beanContext(value);
262         return this;
263      }
264
265      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
266      public Builder beanContext(BeanContext.Builder value) {
267         super.beanContext(value);
268         return this;
269      }
270
271      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
272      public Builder beanDictionary(java.lang.Class<?>...values) {
273         super.beanDictionary(values);
274         return this;
275      }
276
277      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
278      public Builder beanFieldVisibility(Visibility value) {
279         super.beanFieldVisibility(value);
280         return this;
281      }
282
283      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
284      public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) {
285         super.beanInterceptor(on, value);
286         return this;
287      }
288
289      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
290      public Builder beanMapPutReturnsOldValue() {
291         super.beanMapPutReturnsOldValue();
292         return this;
293      }
294
295      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
296      public Builder beanMethodVisibility(Visibility value) {
297         super.beanMethodVisibility(value);
298         return this;
299      }
300
301      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
302      public Builder beanProperties(Map<String,Object> values) {
303         super.beanProperties(values);
304         return this;
305      }
306
307      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
308      public Builder beanProperties(Class<?> beanClass, String properties) {
309         super.beanProperties(beanClass, properties);
310         return this;
311      }
312
313      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
314      public Builder beanProperties(String beanClassName, String properties) {
315         super.beanProperties(beanClassName, properties);
316         return this;
317      }
318
319      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
320      public Builder beanPropertiesExcludes(Map<String,Object> values) {
321         super.beanPropertiesExcludes(values);
322         return this;
323      }
324
325      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
326      public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) {
327         super.beanPropertiesExcludes(beanClass, properties);
328         return this;
329      }
330
331      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
332      public Builder beanPropertiesExcludes(String beanClassName, String properties) {
333         super.beanPropertiesExcludes(beanClassName, properties);
334         return this;
335      }
336
337      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
338      public Builder beanPropertiesReadOnly(Map<String,Object> values) {
339         super.beanPropertiesReadOnly(values);
340         return this;
341      }
342
343      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
344      public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) {
345         super.beanPropertiesReadOnly(beanClass, properties);
346         return this;
347      }
348
349      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
350      public Builder beanPropertiesReadOnly(String beanClassName, String properties) {
351         super.beanPropertiesReadOnly(beanClassName, properties);
352         return this;
353      }
354
355      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
356      public Builder beanPropertiesWriteOnly(Map<String,Object> values) {
357         super.beanPropertiesWriteOnly(values);
358         return this;
359      }
360
361      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
362      public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) {
363         super.beanPropertiesWriteOnly(beanClass, properties);
364         return this;
365      }
366
367      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
368      public Builder beanPropertiesWriteOnly(String beanClassName, String properties) {
369         super.beanPropertiesWriteOnly(beanClassName, properties);
370         return this;
371      }
372
373      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
374      public Builder beansRequireDefaultConstructor() {
375         super.beansRequireDefaultConstructor();
376         return this;
377      }
378
379      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
380      public Builder beansRequireSerializable() {
381         super.beansRequireSerializable();
382         return this;
383      }
384
385      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
386      public Builder beansRequireSettersForGetters() {
387         super.beansRequireSettersForGetters();
388         return this;
389      }
390
391      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
392      public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) {
393         super.dictionaryOn(on, values);
394         return this;
395      }
396
397      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
398      public Builder disableBeansRequireSomeProperties() {
399         super.disableBeansRequireSomeProperties();
400         return this;
401      }
402
403      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
404      public Builder disableIgnoreMissingSetters() {
405         super.disableIgnoreMissingSetters();
406         return this;
407      }
408
409      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
410      public Builder disableIgnoreTransientFields() {
411         super.disableIgnoreTransientFields();
412         return this;
413      }
414
415      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
416      public Builder disableIgnoreUnknownNullBeanProperties() {
417         super.disableIgnoreUnknownNullBeanProperties();
418         return this;
419      }
420
421      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
422      public Builder disableInterfaceProxies() {
423         super.disableInterfaceProxies();
424         return this;
425      }
426
427      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
428      public <T> Builder example(Class<T> pojoClass, T o) {
429         super.example(pojoClass, o);
430         return this;
431      }
432
433      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
434      public <T> Builder example(Class<T> pojoClass, String json) {
435         super.example(pojoClass, json);
436         return this;
437      }
438
439      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
440      public Builder findFluentSetters() {
441         super.findFluentSetters();
442         return this;
443      }
444
445      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
446      public Builder findFluentSetters(Class<?> on) {
447         super.findFluentSetters(on);
448         return this;
449      }
450
451      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
452      public Builder ignoreInvocationExceptionsOnGetters() {
453         super.ignoreInvocationExceptionsOnGetters();
454         return this;
455      }
456
457      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
458      public Builder ignoreInvocationExceptionsOnSetters() {
459         super.ignoreInvocationExceptionsOnSetters();
460         return this;
461      }
462
463      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
464      public Builder ignoreUnknownBeanProperties() {
465         super.ignoreUnknownBeanProperties();
466         return this;
467      }
468
469      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
470      public Builder ignoreUnknownEnumValues() {
471         super.ignoreUnknownEnumValues();
472         return this;
473      }
474
475      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
476      public Builder implClass(Class<?> interfaceClass, Class<?> implClass) {
477         super.implClass(interfaceClass, implClass);
478         return this;
479      }
480
481      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
482      public Builder implClasses(Map<Class<?>,Class<?>> values) {
483         super.implClasses(values);
484         return this;
485      }
486
487      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
488      public Builder interfaceClass(Class<?> on, Class<?> value) {
489         super.interfaceClass(on, value);
490         return this;
491      }
492
493      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
494      public Builder interfaces(java.lang.Class<?>...value) {
495         super.interfaces(value);
496         return this;
497      }
498
499      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
500      public Builder locale(Locale value) {
501         super.locale(value);
502         return this;
503      }
504
505      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
506      public Builder mediaType(MediaType value) {
507         super.mediaType(value);
508         return this;
509      }
510
511      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
512      public Builder notBeanClasses(java.lang.Class<?>...values) {
513         super.notBeanClasses(values);
514         return this;
515      }
516
517      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
518      public Builder notBeanPackages(String...values) {
519         super.notBeanPackages(values);
520         return this;
521      }
522
523      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
524      public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) {
525         super.propertyNamer(value);
526         return this;
527      }
528
529      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
530      public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) {
531         super.propertyNamer(on, value);
532         return this;
533      }
534
535      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
536      public Builder sortProperties() {
537         super.sortProperties();
538         return this;
539      }
540
541      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
542      public Builder sortProperties(java.lang.Class<?>...on) {
543         super.sortProperties(on);
544         return this;
545      }
546
547      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
548      public Builder stopClass(Class<?> on, Class<?> value) {
549         super.stopClass(on, value);
550         return this;
551      }
552
553      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
554      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) {
555         super.swap(normalClass, swappedClass, swapFunction);
556         return this;
557      }
558
559      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
560      public <T, S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) {
561         super.swap(normalClass, swappedClass, swapFunction, unswapFunction);
562         return this;
563      }
564
565      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
566      public Builder swaps(java.lang.Class<?>...values) {
567         super.swaps(values);
568         return this;
569      }
570
571      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
572      public Builder timeZone(TimeZone value) {
573         super.timeZone(value);
574         return this;
575      }
576
577      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
578      public Builder typeName(Class<?> on, String value) {
579         super.typeName(on, value);
580         return this;
581      }
582
583      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
584      public Builder typePropertyName(String value) {
585         super.typePropertyName(value);
586         return this;
587      }
588
589      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
590      public Builder typePropertyName(Class<?> on, String value) {
591         super.typePropertyName(on, value);
592         return this;
593      }
594
595      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
596      public Builder useEnumNames() {
597         super.useEnumNames();
598         return this;
599      }
600
601      @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */
602      public Builder useJavaBeanIntrospector() {
603         super.useJavaBeanIntrospector();
604         return this;
605      }
606
607      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
608      public Builder autoCloseStreams() {
609         super.autoCloseStreams();
610         return this;
611      }
612
613      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
614      public Builder autoCloseStreams(boolean value) {
615         super.autoCloseStreams(value);
616         return this;
617      }
618
619      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
620      public Builder consumes(String value) {
621         super.consumes(value);
622         return this;
623      }
624
625      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
626      public Builder debugOutputLines(int value) {
627         super.debugOutputLines(value);
628         return this;
629      }
630
631      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
632      public Builder listener(Class<? extends org.apache.juneau.parser.ParserListener> value) {
633         super.listener(value);
634         return this;
635      }
636
637      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
638      public Builder strict() {
639         super.strict();
640         return this;
641      }
642
643      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
644      public Builder strict(boolean value) {
645         super.strict(value);
646         return this;
647      }
648
649      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
650      public Builder trimStrings() {
651         super.trimStrings();
652         return this;
653      }
654
655      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
656      public Builder trimStrings(boolean value) {
657         super.trimStrings(value);
658         return this;
659      }
660
661      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
662      public Builder unbuffered() {
663         super.unbuffered();
664         return this;
665      }
666
667      @Override /* GENERATED - org.apache.juneau.parser.Parser.Builder */
668      public Builder unbuffered(boolean value) {
669         super.unbuffered(value);
670         return this;
671      }
672
673      @Override /* GENERATED - org.apache.juneau.parser.ReaderParser.Builder */
674      public Builder fileCharset(Charset value) {
675         super.fileCharset(value);
676         return this;
677      }
678
679      @Override /* GENERATED - org.apache.juneau.parser.ReaderParser.Builder */
680      public Builder streamCharset(Charset value) {
681         super.streamCharset(value);
682         return this;
683      }
684
685      @Override /* GENERATED - org.apache.juneau.uon.UonParser.Builder */
686      public Builder decoding() {
687         super.decoding();
688         return this;
689      }
690
691      @Override /* GENERATED - org.apache.juneau.uon.UonParser.Builder */
692      public Builder decoding(boolean value) {
693         super.decoding(value);
694         return this;
695      }
696
697      @Override /* GENERATED - org.apache.juneau.uon.UonParser.Builder */
698      public Builder validateEnd() {
699         super.validateEnd();
700         return this;
701      }
702
703      @Override /* GENERATED - org.apache.juneau.uon.UonParser.Builder */
704      public Builder validateEnd(boolean value) {
705         super.validateEnd(value);
706         return this;
707      }
708
709      // </FluentSetters>
710   }
711
712   //-------------------------------------------------------------------------------------------------------------------
713   // Instance
714   //-------------------------------------------------------------------------------------------------------------------
715
716   final boolean expandedParams;
717
718   private final Map<ClassMeta<?>,UrlEncodingClassMeta> urlEncodingClassMetas = new ConcurrentHashMap<>();
719   private final Map<BeanPropertyMeta,UrlEncodingBeanPropertyMeta> urlEncodingBeanPropertyMetas = new ConcurrentHashMap<>();
720
721   /**
722    * Constructor.
723    *
724    * @param builder The builder for this object.
725    */
726   public UrlEncodingParser(Builder builder) {
727      super(builder);
728      expandedParams = builder.expandedParams;
729   }
730
731   @Override /* Context */
732   public Builder copy() {
733      return new Builder(this);
734   }
735
736   @Override /* Context */
737   public UrlEncodingParserSession.Builder createSession() {
738      return UrlEncodingParserSession.create(this);
739   }
740
741   @Override /* Context */
742   public UrlEncodingParserSession getSession() {
743      return createSession().build();
744   }
745
746   //-----------------------------------------------------------------------------------------------------------------
747   // Extended metadata
748   //-----------------------------------------------------------------------------------------------------------------
749
750   @Override /* UrlEncodingMetaProvider */
751   public UrlEncodingClassMeta getUrlEncodingClassMeta(ClassMeta<?> cm) {
752      UrlEncodingClassMeta m = urlEncodingClassMetas.get(cm);
753      if (m == null) {
754         m = new UrlEncodingClassMeta(cm, this);
755         urlEncodingClassMetas.put(cm, m);
756      }
757      return m;
758   }
759
760   @Override /* UrlEncodingMetaProvider */
761   public UrlEncodingBeanPropertyMeta getUrlEncodingBeanPropertyMeta(BeanPropertyMeta bpm) {
762      if (bpm == null)
763         return UrlEncodingBeanPropertyMeta.DEFAULT;
764      UrlEncodingBeanPropertyMeta m = urlEncodingBeanPropertyMetas.get(bpm);
765      if (m == null) {
766         m = new UrlEncodingBeanPropertyMeta(bpm.getDelegateFor(), this);
767         urlEncodingBeanPropertyMetas.put(bpm, m);
768      }
769      return m;
770   }
771
772   //-----------------------------------------------------------------------------------------------------------------
773   // Properties
774   //-----------------------------------------------------------------------------------------------------------------
775
776   /**
777    * Parser bean property collections/arrays as separate key/value pairs.
778    *
779    * @see Builder#expandedParams()
780    * @return
781    * <jk>false</jk> if serializing the array <c>[1,2,3]</c> results in <c>?key=$a(1,2,3)</c>.
782    * <br><jk>true</jk> if serializing the same array results in <c>?key=1&amp;key=2&amp;key=3</c>.
783    */
784   protected final boolean isExpandedParams() {
785      return expandedParams;
786   }
787
788   //-----------------------------------------------------------------------------------------------------------------
789   // Other methods
790   //-----------------------------------------------------------------------------------------------------------------
791
792   @Override /* Context */
793   protected JsonMap properties() {
794      return filteredMap("expandedParams", expandedParams);
795   }
796}