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