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