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.httppart;
018
019import static java.util.Collections.*;
020import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
021import static org.apache.juneau.commons.utils.ClassUtils.*;
022import static org.apache.juneau.commons.utils.CollectionUtils.*;
023import static org.apache.juneau.commons.utils.StringUtils.*;
024import static org.apache.juneau.commons.utils.ThrowableUtils.*;
025import static org.apache.juneau.commons.utils.Utils.*;
026import static org.apache.juneau.Constants.*;
027import static org.apache.juneau.httppart.HttpPartDataType.*;
028import static org.apache.juneau.httppart.HttpPartFormat.*;
029
030import java.lang.annotation.*;
031import java.lang.reflect.*;
032import java.math.*;
033import java.util.*;
034import java.util.concurrent.atomic.*;
035import java.util.function.*;
036import java.util.regex.*;
037
038import org.apache.juneau.*;
039import org.apache.juneau.annotation.*;
040import org.apache.juneau.collections.*;
041import org.apache.juneau.commons.collections.*;
042import org.apache.juneau.commons.lang.*;
043import org.apache.juneau.commons.reflect.*;
044import org.apache.juneau.commons.utils.*;
045import org.apache.juneau.http.annotation.*;
046import org.apache.juneau.parser.*;
047
048/**
049 * Represents an OpenAPI schema definition.
050 *
051 * <p>
052 * The schema definition can be applied to any HTTP parts such as bodies, headers, query/form parameters, and URL path parts.
053 * <br>The API is generic enough to apply to any path part although some attributes may only applicable for certain parts.
054 *
055 * <p>
056 * Schema objects are created via builders instantiated through the {@link #create()} method.
057 *
058 * <h5 class='section'>Jakarta Bean Validation Support:</h5>
059 * <p>
060 * As of 9.2.0, this class supports Jakarta Bean Validation constraint annotations (e.g., <c>@NotNull</c>, <c>@Size</c>, <c>@Min</c>, <c>@Max</c>).
061 * When these annotations are encountered during schema building, they are automatically mapped to corresponding OpenAPI schema properties:
062 * <ul>
063 *    <li><c>@NotNull</c> → <c>required(true)</c>
064 *    <li><c>@Size(min=x, max=y)</c> → <c>minLength/maxLength</c> and <c>minItems/maxItems</c>
065 *    <li><c>@Min(value)</c> → <c>minimum(value)</c>
066 *    <li><c>@Max(value)</c> → <c>maximum(value)</c>
067 *    <li><c>@Pattern(regexp)</c> → <c>pattern(regexp)</c>
068 *    <li><c>@Email</c> → <c>format("email")</c>
069 *    <li><c>@Positive/@PositiveOrZero/@Negative/@NegativeOrZero</c> → Corresponding min/max constraints
070 *    <li><c>@NotEmpty</c> → <c>required(true) + minLength(1)/minItems(1)</c>
071 *    <li><c>@NotBlank</c> → <c>required(true) + minLength(1) + pattern</c>
072 *    <li><c>@DecimalMin/@DecimalMax</c> → <c>minimum/maximum</c> with optional <c>exclusiveMinimum/exclusiveMaximum</c>
073 * </ul>
074 * <p>
075 * This integration uses pure reflection and does not require <c>jakarta.validation-api</c> as a dependency.
076 * The annotations are detected and processed automatically when present.
077 *
078 * <h5 class='section'>Notes:</h5><ul>
079 *    <li class='note'>This class is thread safe and reusable.
080 * </ul>
081 *
082 * <h5 class='section'>See Also:</h5><ul>
083 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/OpenApiBasics">OpenApi Basics</a>
084 * </ul>
085 */
086public class HttpPartSchema {
087
088   private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
089
090   /**
091    * Builder class.
092    */
093   public static class Builder {
094      String name, default_;
095      Set<Integer> codes;
096      Set<String> enum_;
097      Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty;
098      HttpPartCollectionFormat collectionFormat = HttpPartCollectionFormat.NO_COLLECTION_FORMAT;
099      HttpPartDataType type = HttpPartDataType.NO_TYPE;
100      HttpPartFormat format = HttpPartFormat.NO_FORMAT;
101      Pattern pattern;
102      Number maximum, minimum, multipleOf;
103      Long maxLength, minLength, maxItems, minItems, maxProperties, minProperties;
104      Map<String,Object> properties;
105      Object items, additionalProperties;
106      boolean noValidate;
107      Class<? extends HttpPartParser> parser;
108      Class<? extends HttpPartSerializer> serializer;
109      // JSON Schema Draft 2020-12 properties
110      String const_;
111      String[] examples;
112      Boolean deprecated;
113      Number exclusiveMaximumValue, exclusiveMinimumValue;
114
115      /**
116       * <mk>const</mk> field (JSON Schema Draft 2020-12).
117       *
118       * <p>
119       * Defines a constant value for this schema.
120       * The instance must be equal to this value to validate.
121       *
122       * @param value
123       *    The new value for this property.
124       * @return This object.
125       */
126      public Builder const_(String value) {
127         const_ = value;
128         return this;
129      }
130
131      /**
132       * <mk>default</mk> field.
133       *
134       * <p>
135       * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request.
136       * <br>(Note: "default" has no meaning for required parameters.)
137       *
138       * <p>
139       * Applicable to the following Swagger schema objects:
140       * <ul>
141       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
142       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
143       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
144       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
145       * </ul>
146       *
147       * @param value
148       *    The new value for this property.
149       *    <br>Ignored if value is <jk>null</jk>.
150       * @return This object.
151       */
152      public Builder default_(String value) {
153         if (ne(value))
154            default_ = value;
155         return this;
156      }
157
158      /**
159       * <mk>enum</mk> field.
160       *
161       * <p>
162       * If specified, the input validates successfully if it is equal to one of the elements in this array.
163       *
164       * <p>
165       * Applicable to the following Swagger schema objects:
166       * <ul>
167       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
168       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
169       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
170       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
171       * </ul>
172       *
173       * @param value
174       *    The new value for this property.
175       *    <br>Ignored if value is <jk>null</jk> or an empty set.
176       * @return This object.
177       */
178      public Builder enum_(Set<String> value) {
179         if (nn(value) && ! value.isEmpty())
180            enum_ = value;
181         return this;
182      }
183
184      /**
185       * <mk>enum</mk> field.
186       *
187       * <p>
188       * Same as {@link #enum_(Set)} but takes in a var-args array.
189       *
190       * @param values
191       *    The new values for this property.
192       *    <br>Ignored if value is empty.
193       * @return This object.
194       */
195      public Builder enum_(String...values) {
196         return enum_(set(values));
197      }
198
199      /**
200       * <mk>additionalProperties</mk> field.
201       *
202       * <p>
203       * Applicable to the following Swagger schema objects:
204       * <ul>
205       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
206       * </ul>
207       *
208       * @param value
209       *    The new value for this property.
210       *    <br>Ignored if value is <jk>null</jk> or empty.
211       * @return This object.
212       */
213      public Builder additionalProperties(Builder value) {
214         if (nn(value))
215            additionalProperties = value;
216         return this;
217      }
218
219      /**
220       * <mk>additionalProperties</mk> field.
221       *
222       * <p>
223       * Applicable to the following Swagger schema objects:
224       * <ul>
225       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
226       * </ul>
227       *
228       * @param value
229       *    The new value for this property.
230       *    <br>Ignored if value is <jk>null</jk> or empty.
231       * @return This object.
232       */
233      public Builder additionalProperties(HttpPartSchema value) {
234         if (nn(value))
235            additionalProperties = value;
236         return this;
237      }
238
239      /**
240       * Synonym for {@link #allowEmptyValue()}.
241       *
242       * @return This object.
243       */
244      public Builder aev() {
245         return allowEmptyValue(true);
246      }
247
248      /**
249       * Synonym for {@link #allowEmptyValue(Boolean)}.
250       *
251       * @param value
252       *    The new value for this property.
253       * @return This object.
254       */
255      public Builder aev(Boolean value) {
256         return allowEmptyValue(value);
257      }
258
259      /**
260       * Synonym for {@link #allowEmptyValue(String)}.
261       *
262       * @param value
263       *    The new value for this property.
264       * @return This object.
265       */
266      public Builder aev(String value) {
267         return allowEmptyValue(value);
268      }
269
270      /**
271       * <mk>allowEmptyValue</mk> field.
272       *
273       * <p>
274       * Shortcut for calling <code>allowEmptyValue(<jk>true</jk>);</code>.
275       *
276       * @return This object.
277       */
278      public Builder allowEmptyValue() {
279         return allowEmptyValue(true);
280      }
281
282      /**
283       * <mk>allowEmptyValue</mk> field.
284       *
285       * <p>
286       * Sets the ability to pass empty-valued parameters.
287       * <br>This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value.
288       * <br>The default value is <jk>false</jk>.
289       *
290       * <p>
291       * Applicable to the following Swagger schema objects:
292       * <ul>
293       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
294       * </ul>
295       *
296       * @param value
297       *    The new value for this property.
298       *    <br>Ignored if value is <jk>null</jk>.
299       * @return This object.
300       */
301      public Builder allowEmptyValue(Boolean value) {
302         allowEmptyValue = resolve(value, allowEmptyValue);
303         return this;
304      }
305
306      /**
307       * <mk>allowEmptyValue</mk> field.
308       *
309       * <p>
310       * Same as {@link #allowEmptyValue(Boolean)} but takes in a string boolean value.
311       *
312       * @param value
313       *    The new value for this property.
314       *    <br>Ignored if value is <jk>null</jk> or empty.
315       * @return This object.
316       */
317      public Builder allowEmptyValue(String value) {
318         allowEmptyValue = resolve(value, allowEmptyValue);
319         return this;
320      }
321
322      /**
323       * Shortcut for <c>additionalProperties(value)</c>
324       *
325       * <p>
326       * Applicable to the following Swagger schema objects:
327       * <ul>
328       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
329       * </ul>
330       *
331       * @param value
332       *    The new value for this property.
333       *    <br>Ignored if value is <jk>null</jk> or empty.
334       * @return This object.
335       */
336      public Builder ap(Builder value) {
337         return additionalProperties(value);
338      }
339
340      /**
341       * Shortcut for <c>additionalProperties(value)</c>
342       *
343       * <p>
344       * Applicable to the following Swagger schema objects:
345       * <ul>
346       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
347       * </ul>
348       *
349       * @param value
350       *    The new value for this property.
351       *    <br>Ignored if value is <jk>null</jk> or empty.
352       * @return This object.
353       */
354      public Builder ap(HttpPartSchema value) {
355         return additionalProperties(value);
356      }
357
358      /**
359       * Apply the specified annotation to this schema.
360       *
361       * @param a The annotation to apply.
362       * @return This object.
363      */
364      public Builder apply(Annotation a) {
365         if (a instanceof Content a2)
366            apply(a2);
367         else if (a instanceof Header a2)
368            apply(a2);
369         else if (a instanceof FormData a2)
370            apply(a2);
371         else if (a instanceof Query a3)
372            apply(a3);
373         else if (a instanceof Path a4)
374            apply(a4);
375         else if (a instanceof PathRemainder a5)
376            apply(a5);
377         else if (a instanceof Response a6)
378            apply(a6);
379         else if (a instanceof StatusCode a7)
380            apply(a7);
381         else if (a instanceof HasQuery a8)
382            apply(a8);
383         else if (a instanceof HasFormData a9)
384            apply(a9);
385         else if (a instanceof Schema a10)
386            apply(a10);
387         else if (cn(a.annotationType()).startsWith("jakarta.validation.constraints."))
388            applyJakartaValidation(a);
389         else
390            throw rex("Builder.apply(@{0}) not defined", cn(a));
391         return this;
392      }
393
394      /**
395       * Instantiates a new {@link HttpPartSchema} object based on the configuration of this builder.
396       *
397       * <p>
398       * This method can be called multiple times to produce new schema objects.
399       *
400       * @return
401       *    A new {@link HttpPartSchema} object.
402       *    <br>Never <jk>null</jk>.
403       */
404      public HttpPartSchema build() {
405         return new HttpPartSchema(this);
406      }
407
408      /**
409       * Synonym for {@link #collectionFormat(HttpPartCollectionFormat)}.
410       *
411       * @param value
412       *    The new value for this property.
413       * @return This object.
414       */
415      public Builder cf(HttpPartCollectionFormat value) {
416         return collectionFormat(value);
417      }
418
419      /**
420       * Synonym for {@link #collectionFormat(String)}.
421       *
422       * @param value
423       *    The new value for this property.
424       * @return This object.
425       */
426      public Builder cf(String value) {
427         return collectionFormat(value);
428      }
429
430      /**
431       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.CSV)</c>.
432       *
433       * @return This object.
434       */
435      public Builder cfCsv() {
436         return collectionFormat(HttpPartCollectionFormat.CSV);
437      }
438
439      /**
440       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.MULTI)</c>.
441       *
442       * @return This object.
443       */
444      public Builder cfMulti() {
445         return collectionFormat(HttpPartCollectionFormat.MULTI);
446      }
447
448      /**
449       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT)</c>.
450       *
451       * @return This object.
452       */
453      public Builder cfNone() {
454         return collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT);
455      }
456
457      /**
458       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.PIPES)</c>.
459       *
460       * @return This object.
461       */
462      public Builder cfPipes() {
463         return collectionFormat(HttpPartCollectionFormat.PIPES);
464      }
465
466      /**
467       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.SSV)</c>.
468       *
469       * @return This object.
470       */
471      public Builder cfSsv() {
472         return collectionFormat(HttpPartCollectionFormat.SSV);
473      }
474
475      /**
476       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.TSV)</c>.
477       *
478       * @return This object.
479       */
480      public Builder cfTsv() {
481         return collectionFormat(HttpPartCollectionFormat.TSV);
482      }
483
484      /**
485       * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.UONC)</c>.
486       *
487       * @return This object.
488       */
489      public Builder cfUon() {
490         return collectionFormat(HttpPartCollectionFormat.UONC);
491      }
492
493      /**
494       * <mk>httpStatusCode</mk> key.
495       *
496       * <p>
497       * Applicable to the following Swagger schema objects:
498       * <ul>
499       *    <li><a class="doclink" href="https://swagger.io/specification/v2#responsesObject">Responses</a>
500       * </ul>
501       *
502       * @param value
503       *    The new value for this property.
504       *    <br>Ignored if value is <c>0</c>.
505       * @return This object.
506       */
507      public Builder code(int value) {
508         if (value != 0) {
509            if (codes == null)
510               codes = new TreeSet<>();
511            codes.add(value);
512         }
513         return this;
514      }
515
516      /**
517       * <mk>httpStatusCode</mk> key.
518       *
519       * <p>
520       * Applicable to the following Swagger schema objects:
521       * <ul>
522       *    <li><a class="doclink" href="https://swagger.io/specification/v2#responsesObject">Responses</a>
523       * </ul>
524       *
525       * @param value
526       *    The new value for this property.
527       *    <br>Ignored if <jk>null</jk> or an empty array.
528       * @return This object.
529       */
530      public Builder codes(int[] value) {
531         if (nn(value) && value.length != 0)
532            for (var v : value)
533               code(v);
534         return this;
535      }
536
537      /**
538       * <mk>collectionFormat</mk> field.
539       *
540       * <p>
541       * Determines the format of the array if <c>type</c> <js>"array"</js> is used.
542       * <br>Can only be used if <c>type</c> is <js>"array"</js>.
543       *
544       * <p>
545       * Applicable to the following Swagger schema objects:
546       * <ul>
547       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
548       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
549       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
550       * </ul>
551       *
552       * <p>
553       * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
554       *
555       * <ul class='values javatree'>
556       *    <ul class='jc'>{@link HttpPartCollectionFormat}
557       *    <ul>
558       *       <li>
559       *          {@link HttpPartCollectionFormat#CSV CSV} (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
560       *       <li>
561       *          {@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. <js>"foo bar"</js>).
562       *       <li>
563       *          {@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. <js>"foo\tbar"</js>).
564       *       <li>
565       *          {@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. <js>"foo|bar"</js>).
566       *       <li>
567       *          {@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&amp;foo=baz"</js>).
568       *       <li>
569       *          {@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. <js>"@(foo,bar)"</js>).
570       *    </ul>
571       * </ul>
572       *
573       * @param value
574       *    The new value for this property.
575       * @return This object.
576       */
577      public Builder collectionFormat(HttpPartCollectionFormat value) {
578         collectionFormat = value;
579         return this;
580      }
581
582      /**
583       * <mk>collectionFormat</mk> field.
584       *
585       * <p>
586       * Determines the format of the array if <c>type</c> <js>"array"</js> is used.
587       * <br>Can only be used if <c>type</c> is <js>"array"</js>.
588       *
589       * <p>
590       * Applicable to the following Swagger schema objects:
591       * <ul>
592       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
593       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
594       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
595       * </ul>
596       *
597       * <p>
598       * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements.
599       *
600       * <ul class='values'>
601       *    <li>
602       *       <js>"csv"</js> (default) - Comma-separated values (e.g. <js>"foo,bar"</js>).
603       *    <li>
604       *       <js>"ssv"</js> - Space-separated values (e.g. <js>"foo bar"</js>).
605       *    <li>
606       *       <js>"tsv"</js> - Tab-separated values (e.g. <js>"foo\tbar"</js>).
607       *    <li>
608       *       <js>"pipes</js> - Pipe-separated values (e.g. <js>"foo|bar"</js>).
609       *    <li>
610       *       <js>"multi"</js> - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&amp;foo=baz"</js>).
611       *    <li>
612       *       <js>"uon"</js> - UON notation (e.g. <js>"@(foo,bar)"</js>).
613       *    <li>
614       * </ul>
615       *
616       * @param value
617       *    The new value for this property.
618       *    <br>Ignored if value is <jk>null</jk> or empty.
619       * @return This object.
620       */
621      public Builder collectionFormat(String value) {
622         try {
623            if (ne(value))
624               this.collectionFormat = HttpPartCollectionFormat.fromString(value);
625         } catch (Exception e) {
626            throw new ContextRuntimeException(e, "Invalid value ''{0}'' passed in as collectionFormat value.  Valid values: {1}", value, HttpPartCollectionFormat.values());
627         }
628         return this;
629      }
630
631      /**
632       * <mk>deprecated</mk> field (JSON Schema Draft 2020-12).
633       *
634       * <p>
635       * Indicates that applications should refrain from usage of this property.
636       * This is used for documentation purposes only and does not affect validation.
637       *
638       * @param value
639       *    The new value for this property.
640       * @return This object.
641       */
642      public Builder deprecated(Boolean value) {
643         deprecated = resolve(value, deprecated);
644         return this;
645      }
646
647      /**
648       * Synonym for {@link #default_(String)}.
649       *
650       * @param value
651       *    The new value for this property.
652       * @return This object.
653       */
654      public Builder df(String value) {
655         return default_(value);
656      }
657
658      /**
659       * Synonym for {@link #enum_(Set)}.
660       *
661       * @param value
662       *    The new value for this property.
663       * @return This object.
664       */
665      public Builder e(Set<String> value) {
666         return enum_(value);
667      }
668
669      /**
670       * Synonym for {@link #enum_(String...)}.
671       *
672       * @param values
673       *    The new values for this property.
674       * @return This object.
675       */
676      public Builder e(String...values) {
677         return enum_(values);
678      }
679
680      /**
681       * Synonym for {@link #exclusiveMaximum()}.
682       *
683       * @return This object.
684       */
685      public Builder emax() {
686         return exclusiveMaximum();
687      }
688
689      /**
690       * Synonym for {@link #exclusiveMaximum(Boolean)}.
691       *
692       * @param value
693       *    The new value for this property.
694       * @return This object.
695       */
696      public Builder emax(Boolean value) {
697         return exclusiveMaximum(value);
698      }
699
700      /**
701       * Synonym for {@link #exclusiveMaximum(String)}.
702       *
703       * @param value
704       *    The new value for this property.
705       * @return This object.
706       */
707      public Builder emax(String value) {
708         return exclusiveMaximum(value);
709      }
710
711      /**
712       * Synonym for {@link #exclusiveMinimum()}.
713       *
714       * @return This object.
715       */
716      public Builder emin() {
717         return exclusiveMinimum();
718      }
719
720      /**
721       * Synonym for {@link #exclusiveMinimum(Boolean)}.
722       *
723       * @param value
724       *    The new value for this property.
725       * @return This object.
726       */
727      public Builder emin(Boolean value) {
728         return exclusiveMinimum(value);
729      }
730
731      /**
732       * Synonym for {@link #exclusiveMinimum(String)}.
733       *
734       * @param value
735       *    The new value for this property.
736       * @return This object.
737       */
738      public Builder emin(String value) {
739         return exclusiveMinimum(value);
740      }
741
742      /**
743       * <mk>examples</mk> field (JSON Schema Draft 2020-12).
744       *
745       * <p>
746       * An array of example values.
747       * This is used for documentation purposes only and does not affect validation.
748       *
749       * @param value
750       *    The new value for this property.
751       * @return This object.
752       */
753      public Builder examples(String...value) {
754         examples = value;
755         return this;
756      }
757
758      /**
759       * <mk>exclusiveMaximum</mk> field.
760       *
761       * <p>
762       * Shortcut for calling <code>exclusiveMaximum(<jk>true</jk>);</code>.
763       *
764       * @return This object.
765       */
766      public Builder exclusiveMaximum() {
767         return exclusiveMaximum(true);
768      }
769
770      /**
771       * <mk>exclusiveMaximum</mk> field.
772       *
773       * <p>
774       * Defines whether the maximum is matched exclusively.
775       *
776       * <p>
777       * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
778       * <br>If <jk>true</jk>, must be accompanied with <c>maximum</c>.
779       *
780       * <p>
781       * Applicable to the following Swagger schema objects:
782       * <ul>
783       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
784       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
785       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
786       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
787       * </ul>
788       *
789       * @param value
790       *    The new value for this property.
791       *    <br>Ignored if value is <jk>null</jk>.
792       * @return This object.
793       */
794      public Builder exclusiveMaximum(Boolean value) {
795         exclusiveMaximum = resolve(value, exclusiveMaximum);
796         return this;
797      }
798
799      /**
800       * <mk>exclusiveMaximum</mk> field.
801       *
802       * <p>
803       * Same as {@link #exclusiveMaximum(Boolean)} but takes in a string boolean value.
804       *
805       * @param value
806       *    The new value for this property.
807       *    <br>Ignored if value is <jk>null</jk> or empty.
808       * @return This object.
809       */
810      public Builder exclusiveMaximum(String value) {
811         exclusiveMaximum = resolve(value, exclusiveMaximum);
812         return this;
813      }
814
815      /**
816       * <mk>exclusiveMaximum</mk> field with numeric value (JSON Schema Draft 2020-12).
817       *
818       * <p>
819       * Defines the exclusive maximum value for numeric types.
820       * The instance is valid if it is strictly less than (not equal to) this value.
821       *
822       * <p>
823       * This is the Draft 2020-12 version that uses a numeric value instead of a boolean flag.
824       * If this is set, it takes precedence over the boolean {@link #exclusiveMaximum(Boolean)} property.
825       *
826       * @param value
827       *    The new value for this property.
828       * @return This object.
829       */
830      public Builder exclusiveMaximumValue(Number value) {
831         exclusiveMaximumValue = value;
832         return this;
833      }
834
835      /**
836       * <mk>exclusiveMinimum</mk> field.
837       *
838       * <p>
839       * Shortcut for calling <code>exclusiveMinimum(<jk>true</jk>);</code>.
840       *
841       * @return This object.
842       */
843      public Builder exclusiveMinimum() {
844         return exclusiveMinimum(true);
845      }
846
847      /**
848       * <mk>exclusiveMinimum</mk> field.
849       *
850       * <p>
851       * Defines whether the minimum is matched exclusively.
852       *
853       * <p>
854       * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
855       * <br>If <jk>true</jk>, must be accompanied with <c>minimum</c>.
856       *
857       * <p>
858       * Applicable to the following Swagger schema objects:
859       * <ul>
860       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
861       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
862       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
863       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
864       * </ul>
865       *
866       * @param value
867       *    The new value for this property.
868       *    <br>Ignored if value is <jk>null</jk>.
869       * @return This object.
870       */
871      public Builder exclusiveMinimum(Boolean value) {
872         exclusiveMinimum = resolve(value, exclusiveMinimum);
873         return this;
874      }
875
876      /**
877       * <mk>exclusiveMinimum</mk> field.
878       *
879       * <p>
880       * Same as {@link #exclusiveMinimum(Boolean)} but takes in a string boolean value.
881       *
882       * @param value
883       *    The new value for this property.
884       *    <br>Ignored if value is <jk>null</jk> or empty.
885       * @return This object.
886       */
887      public Builder exclusiveMinimum(String value) {
888         exclusiveMinimum = resolve(value, exclusiveMinimum);
889         return this;
890      }
891
892      /**
893       * <mk>exclusiveMinimum</mk> field with numeric value (JSON Schema Draft 2020-12).
894       *
895       * <p>
896       * Defines the exclusive minimum value for numeric types.
897       * The instance is valid if it is strictly greater than (not equal to) this value.
898       *
899       * <p>
900       * This is the Draft 2020-12 version that uses a numeric value instead of a boolean flag.
901       * If this is set, it takes precedence over the boolean {@link #exclusiveMinimum(Boolean)} property.
902       *
903       * @param value
904       *    The new value for this property.
905       * @return This object.
906       */
907      public Builder exclusiveMinimumValue(Number value) {
908         exclusiveMinimumValue = value;
909         return this;
910      }
911
912      /**
913       * Synonym for {@link #format(HttpPartFormat)}.
914       *
915       * @param value
916       *    The new value for this property.
917       * @return This object.
918       */
919      public Builder f(HttpPartFormat value) {
920         return format(value);
921      }
922
923      /**
924       * Synonym for {@link #format(String)}.
925       *
926       * @param value
927       *    The new value for this property.
928       * @return This object.
929       */
930      public Builder f(String value) {
931         return format(value);
932      }
933
934      /**
935       * Shortcut for <c>format(HttpPartFormat.BINARY)</c>.
936       *
937       * @return This object.
938       */
939      public Builder fBinary() {
940         format = HttpPartFormat.BINARY;
941         return this;
942      }
943
944      /**
945       * Shortcut for <c>format(HttpPartFormat.BINARY_SPACED)</c>.
946       *
947       * @return This object.
948       */
949      public Builder fBinarySpaced() {
950         format = HttpPartFormat.BINARY_SPACED;
951         return this;
952      }
953
954      /**
955       * Shortcut for <c>format(HttpPartFormat.BYTE)</c>.
956       *
957       * @return This object.
958       */
959      public Builder fByte() {
960         format = HttpPartFormat.BYTE;
961         return this;
962      }
963
964      /**
965       * Shortcut for <c>format(HttpPartFormat.DATE)</c>.
966       *
967       * @return This object.
968       */
969      public Builder fDate() {
970         format = HttpPartFormat.DATE;
971         return this;
972      }
973
974      /**
975       * Shortcut for <c>format(HttpPartFormat.DATE_TIME)</c>.
976       *
977       * @return This object.
978       */
979      public Builder fDateTime() {
980         format = HttpPartFormat.DATE_TIME;
981         return this;
982      }
983
984      /**
985       * Shortcut for <c>format(HttpPartFormat.DOUBLE)</c>.
986       *
987       * @return This object.
988       */
989      public Builder fDouble() {
990         format = HttpPartFormat.DOUBLE;
991         return this;
992      }
993
994      /**
995       * Shortcut for <c>format(HttpPartFormat.FLOAT)</c>.
996       *
997       * @return This object.
998       */
999      public Builder fFloat() {
1000         format = HttpPartFormat.FLOAT;
1001         return this;
1002      }
1003
1004      /**
1005       * Shortcut for <c>format(HttpPartFormat.INT32)</c>.
1006       *
1007       * @return This object.
1008       */
1009      public Builder fInt32() {
1010         format = HttpPartFormat.INT32;
1011         return this;
1012      }
1013
1014      /**
1015       * Shortcut for <c>format(HttpPartFormat.INT64)</c>.
1016       *
1017       * @return This object.
1018       */
1019      public Builder fInt64() {
1020         format = HttpPartFormat.INT64;
1021         return this;
1022      }
1023
1024      /**
1025       * Shortcut for <c>format(HttpPartFormat.NO_FORMAT)</c>.
1026       *
1027       * @return This object.
1028       */
1029      public Builder fNone() {
1030         format = HttpPartFormat.NO_FORMAT;
1031         return this;
1032      }
1033
1034      /**
1035       * <mk>format</mk> field.
1036       *
1037       * <p>
1038       * The extending format for the previously mentioned <a class="doclink" href="https://swagger.io/specification/v2#parameterType">parameter type</a>.
1039       *
1040       * <p>
1041       * Applicable to the following Swagger schema objects:
1042       * <ul>
1043       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1044       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1045       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1046       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1047       * </ul>
1048       *
1049       * <ul class='values javatree'>
1050       *    <ul class='jc'>{@link HttpPartFormat}
1051       *    <ul>
1052       *       <li class='jf'>
1053       *          {@link HttpPartFormat#INT32 INT32} - Signed 32 bits.
1054       *          <br>Only valid with type <js>"integer"</js>.
1055       *       <li class='jf'>
1056       *          {@link HttpPartFormat#INT64 INT64} - Signed 64 bits.
1057       *          <br>Only valid with type <js>"integer"</js>.
1058       *       <li class='jf'>
1059       *          {@link HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number.
1060       *          <br>Only valid with type <js>"number"</js>.
1061       *       <li class='jf'>
1062       *          {@link HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number.
1063       *          <br>Only valid with type <js>"number"</js>.
1064       *       <li class='jf'>
1065       *          {@link HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters.
1066       *          <br>Only valid with type <js>"string"</js>.
1067       *          <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1068       *       <li class='jf'>
1069       *          {@link HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
1070       *          <br>Only valid with type <js>"string"</js>.
1071       *          <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1072       *       <li class='jf'>
1073       *          {@link HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Hexadecimal encoded octets, spaced (e.g. <js>"00 FF"</js>).
1074       *          <br>Only valid with type <js>"string"</js>.
1075       *          <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1076       *       <li class='jf'>
1077       *          {@link HttpPartFormat#DATE DATE} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
1078       *          <br>Only valid with type <js>"string"</js>.
1079       *       <li class='jf'>
1080       *          {@link HttpPartFormat#DATE_TIME DATE_TIME} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
1081       *          <br>Only valid with type <js>"string"</js>.
1082       *       <li class='jf'>
1083       *          {@link HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured.
1084       *          <br>This format does not affect the serialization or parsing of the parameter.
1085       *       <li class='jf'>
1086       *          {@link HttpPartFormat#UON UON} - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
1087       *          <br>Only valid with type <js>"object"</js>.
1088       *          <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
1089       *    </ul>
1090       * </ul>
1091       *
1092       * <h5 class='section'>See Also:</h5><ul>
1093       *    <li class='extlink'><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Swagger Schema Object</a>
1094       * </ul>
1095       *
1096       * @param value
1097       *    The new value for this property.
1098       * @return This object.
1099       */
1100      public Builder format(HttpPartFormat value) {
1101         format = value;
1102         return this;
1103      }
1104
1105      /**
1106       * <mk>format</mk> field.
1107       *
1108       * <p>
1109       * The extending format for the previously mentioned <a class="doclink" href="https://swagger.io/specification/v2#parameterType">parameter type</a>.
1110       *
1111       * <p>
1112       * Applicable to the following Swagger schema objects:
1113       * <ul>
1114       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1115       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1116       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1117       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1118       * </ul>
1119       *
1120       * <ul class='values'>
1121       *    <li>
1122       *       <js>"int32"</js> - Signed 32 bits.
1123       *       <br>Only valid with type <js>"integer"</js>.
1124       *    <li>
1125       *       <js>"int64"</js> - Signed 64 bits.
1126       *       <br>Only valid with type <js>"integer"</js>.
1127       *    <li>
1128       *       <js>"float"</js> - 32-bit floating point number.
1129       *       <br>Only valid with type <js>"number"</js>.
1130       *    <li>
1131       *       <js>"double"</js> - 64-bit floating point number.
1132       *       <br>Only valid with type <js>"number"</js>.
1133       *    <li>
1134       *       <js>"byte"</js> - BASE-64 encoded characters.
1135       *       <br>Only valid with type <js>"string"</js>.
1136       *       <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1137       *    <li>
1138       *       <js>"binary"</js> - Hexadecimal encoded octets (e.g. <js>"00FF"</js>).
1139       *       <br>Only valid with type <js>"string"</js>.
1140       *       <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1141       *    <li>
1142       *       <js>"binary-spaced"</js> - Hexadecimal encoded octets, spaced (e.g. <js>"00 FF"</js>).
1143       *       <br>Only valid with type <js>"string"</js>.
1144       *       <br>Parameters of type POJO convertible from string are converted after the string has been decoded.
1145       *    <li>
1146       *       <js>"date"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>.
1147       *       <br>Only valid with type <js>"string"</js>.
1148       *    <li>
1149       *       <js>"date-time"</js> - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>.
1150       *       <br>Only valid with type <js>"string"</js>.
1151       *    <li>
1152       *       <js>"password"</js> - Used to hint UIs the input needs to be obscured.
1153       *       <br>This format does not affect the serialization or parsing of the parameter.
1154       *    <li>
1155       *       <js>"uon"</js> - UON notation (e.g. <js>"(foo=bar,baz=@(qux,123))"</js>).
1156       *       <br>Only valid with type <js>"object"</js>.
1157       *       <br>If not specified, then the input is interpreted as plain-text and is converted to a POJO directly.
1158       * </ul>
1159       *
1160       * <h5 class='section'>See Also:</h5><ul>
1161       *    <li class='extlink'><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Swagger Schema Object</a>
1162       * </ul>
1163       *
1164       * @param value
1165       *    The new value for this property.
1166       *    <br>Ignored if value is <jk>null</jk> or an empty string.
1167       * @return This object.
1168       */
1169      public Builder format(String value) {
1170         try {
1171            if (ne(value))
1172               format = HttpPartFormat.fromString(value);
1173         } catch (Exception e) {
1174            throw new ContextRuntimeException(e, "Invalid value ''{0}'' passed in as format value.  Valid values: {1}", value, HttpPartFormat.values());
1175         }
1176         return this;
1177      }
1178
1179      /**
1180       * Shortcut for <c>format(HttpPartFormat.PASSWORD)</c>.
1181       *
1182       * @return This object.
1183       */
1184      public Builder fPassword() {
1185         format = HttpPartFormat.PASSWORD;
1186         return this;
1187      }
1188
1189      /**
1190       * Shortcut for <c>format(HttpPartFormat.UON)</c>.
1191       *
1192       * @return This object.
1193       */
1194      public Builder fUon() {
1195         format = HttpPartFormat.UON;
1196         return this;
1197      }
1198
1199      /**
1200       * Synonym for {@link #items(HttpPartSchema.Builder)}.
1201       *
1202       * @param value
1203       *    The new value for this property.
1204       * @return This object.
1205       */
1206      public Builder i(Builder value) {
1207         return items(value);
1208      }
1209
1210      /**
1211       * Synonym for {@link #items(HttpPartSchema)}.
1212       *
1213       * @param value
1214       *    The new value for this property.
1215       * @return This object.
1216       */
1217      public Builder i(HttpPartSchema value) {
1218         return items(value);
1219      }
1220
1221      /**
1222       * <mk>items</mk> field.
1223       *
1224       * <p>
1225       * Describes the type of items in the array.
1226       * <p>
1227       * Required if <c>type</c> is <js>"array"</js>.
1228       * <br>Can only be used if <c>type</c> is <js>"array"</js>.
1229       *
1230       * <p>
1231       * Applicable to the following Swagger schema objects:
1232       * <ul>
1233       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1234       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1235       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1236       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1237       * </ul>
1238       *
1239       * @param value
1240       *    The new value for this property.
1241       *    <br>Ignored if value is <jk>null</jk> or empty.
1242       * @return This object.
1243       */
1244      public Builder items(Builder value) {
1245         if (nn(value))
1246            items = value;
1247         return this;
1248      }
1249
1250      /**
1251       * <mk>items</mk> field.
1252       *
1253       * <p>
1254       * Describes the type of items in the array.
1255       * <p>
1256       * Required if <c>type</c> is <js>"array"</js>.
1257       * <br>Can only be used if <c>type</c> is <js>"array"</js>.
1258       *
1259       * <p>
1260       * Applicable to the following Swagger schema objects:
1261       * <ul>
1262       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1263       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1264       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1265       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1266       * </ul>
1267       *
1268       * @param value
1269       *    The new value for this property.
1270       *    <br>Ignored if value is <jk>null</jk> or empty.
1271       * @return This object.
1272       */
1273      public Builder items(HttpPartSchema value) {
1274         if (nn(value))
1275            items = value;
1276         return this;
1277      }
1278
1279      /**
1280       * Synonym for {@link #maximum(Number)}.
1281       *
1282       * @param value
1283       *    The new value for this property.
1284       * @return This object.
1285       */
1286      public Builder max(Number value) {
1287         return maximum(value);
1288      }
1289
1290      /**
1291       * Synonym for {@link #maxItems(Long)}.
1292       *
1293       * @param value
1294       *    The new value for this property.
1295       * @return This object.
1296       */
1297      public Builder maxi(Long value) {
1298         return maxItems(value);
1299      }
1300
1301      /**
1302       * Synonym for {@link #maxItems(String)}.
1303       *
1304       * @param value
1305       *    The new value for this property.
1306       * @return This object.
1307       */
1308      public Builder maxi(String value) {
1309         return maxItems(value);
1310      }
1311
1312      /**
1313       * <mk>maximum</mk> field.
1314       *
1315       * <p>
1316       * Defines the maximum value for a parameter of numeric types.
1317       *
1318       * <p>
1319       * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
1320       *
1321       * <p>
1322       * Applicable to the following Swagger schema objects:
1323       * <ul>
1324       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1325       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1326       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1327       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1328       * </ul>
1329       *
1330       * @param value
1331       *    The new value for this property.
1332       *    <br>Ignored if value is <jk>null</jk>.
1333       * @return This object.
1334       */
1335      public Builder maximum(Number value) {
1336         if (nn(value))
1337            maximum = value;
1338         return this;
1339      }
1340
1341      /**
1342       * <mk>maxItems</mk> field.
1343       *
1344       * <p>
1345       * An array or collection is valid if its size is less than, or equal to, the value of this keyword.
1346       *
1347       * <p>
1348       * Only allowed for the following types: <js>"array"</js>.
1349       *
1350       * <p>
1351       * Applicable to the following Swagger schema objects:
1352       * <ul>
1353       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1354       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1355       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1356       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1357       * </ul>
1358       *
1359       * @param value
1360       *    The new value for this property.
1361       *    <br>Ignored if value is <jk>null</jk> or <c>-1</c>.
1362       * @return This object.
1363       */
1364      public Builder maxItems(Long value) {
1365         maxItems = resolve(value, maxItems);
1366         return this;
1367      }
1368
1369      /**
1370       * <mk>maxItems</mk> field.
1371       *
1372       * <p>
1373       * Same as {@link #maxItems(Long)} but takes in a string number.
1374       *
1375       * @param value
1376       *    The new value for this property.
1377       *    <br>Ignored if value is <jk>null</jk> or empty.
1378       * @return This object.
1379       */
1380      public Builder maxItems(String value) {
1381         maxItems = resolve(value, maxItems);
1382         return this;
1383      }
1384
1385      /**
1386       * Synonym for {@link #maxLength(Long)}.
1387       *
1388       * @param value
1389       *    The new value for this property.
1390       * @return This object.
1391       */
1392      public Builder maxl(Long value) {
1393         return maxLength(value);
1394      }
1395
1396      /**
1397       * Synonym for {@link #maxLength(String)}.
1398       *
1399       * @param value
1400       *    The new value for this property.
1401       * @return This object.
1402       */
1403      public Builder maxl(String value) {
1404         return maxLength(value);
1405      }
1406
1407      /**
1408       * <mk>maxLength</mk> field.
1409       *
1410       * <p>
1411       * A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
1412       * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
1413       *
1414       * <p>
1415       * Only allowed for the following types: <js>"string"</js>.
1416       *
1417       * <p>
1418       * Applicable to the following Swagger schema objects:
1419       * <ul>
1420       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1421       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1422       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1423       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1424       * </ul>
1425       *
1426       * @param value
1427       *    The new value for this property.
1428       *    <br>Ignored if value is <jk>null</jk> or <c>-1</c>.
1429       * @return This object.
1430       */
1431      public Builder maxLength(Long value) {
1432         maxLength = resolve(value, maxLength);
1433         return this;
1434      }
1435
1436      /**
1437       * <mk>maxLength</mk> field.
1438       *
1439       * <p>
1440       * Same as {@link #maxLength(Long)} but takes in a string number.
1441       *
1442       * @param value
1443       *    The new value for this property.
1444       *    <br>Ignored if value is <jk>null</jk> or empty.
1445       * @return This object.
1446       */
1447      public Builder maxLength(String value) {
1448         maxLength = resolve(value, maxLength);
1449         return this;
1450      }
1451
1452      /**
1453       * Synonym for {@link #maxProperties(Long)}.
1454       *
1455       * @param value
1456       *    The new value for this property.
1457       * @return This object.
1458       */
1459      public Builder maxp(Long value) {
1460         return maxProperties(value);
1461      }
1462
1463      /**
1464       * Synonym for {@link #maxProperties(String)}.
1465       *
1466       * @param value
1467       *    The new value for this property.
1468       * @return This object.
1469       */
1470      public Builder maxp(String value) {
1471         return maxProperties(value);
1472      }
1473
1474      /**
1475       * <mk>mapProperties</mk> field.
1476       *
1477       * <p>
1478       * Applicable to the following Swagger schema objects:
1479       * <ul>
1480       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1481       * </ul>
1482       *
1483       * @param value
1484       *    The new value for this property.
1485       *    <br>Ignored if value is <jk>null</jk> or <c>-1</c>.
1486       * @return This object.
1487       */
1488      public Builder maxProperties(Long value) {
1489         maxProperties = resolve(value, maxProperties);
1490         return this;
1491      }
1492
1493      /**
1494       * <mk>mapProperties</mk> field.
1495       *
1496       * <p>
1497       * Same as {@link #maxProperties(Long)} but takes in a string number.
1498       *
1499       * @param value
1500       *    The new value for this property.
1501       *    <br>Ignored if value is <jk>null</jk> or empty.
1502       * @return This object.
1503       */
1504      public Builder maxProperties(String value) {
1505         maxProperties = resolve(value, maxProperties);
1506         return this;
1507      }
1508
1509      /**
1510       * Synonym for {@link #minimum(Number)}.
1511       *
1512       * @param value
1513       *    The new value for this property.
1514       * @return This object.
1515       */
1516      public Builder min(Number value) {
1517         return minimum(value);
1518      }
1519
1520      /**
1521       * Synonym for {@link #minItems(Long)}.
1522       *
1523       * @param value
1524       *    The new value for this property.
1525       * @return This object.
1526       */
1527      public Builder mini(Long value) {
1528         return minItems(value);
1529      }
1530
1531      /**
1532       * Synonym for {@link #minItems(String)}.
1533       *
1534       * @param value
1535       *    The new value for this property.
1536       * @return This object.
1537       */
1538      public Builder mini(String value) {
1539         return minItems(value);
1540      }
1541
1542      /**
1543       * <mk>minimum</mk> field.
1544       *
1545       * <p>
1546       * Defines the minimum value for a parameter of numeric types.
1547       *
1548       * <p>
1549       * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
1550       *
1551       * <p>
1552       * Applicable to the following Swagger schema objects:
1553       * <ul>
1554       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1555       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1556       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1557       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1558       * </ul>
1559       *
1560       * @param value
1561       *    The new value for this property.
1562       *    <br>Ignored if value is <jk>null</jk>.
1563       * @return This object.
1564       */
1565      public Builder minimum(Number value) {
1566         if (nn(value))
1567            minimum = value;
1568         return this;
1569      }
1570
1571      /**
1572       * <mk>minItems</mk> field.
1573       *
1574       * <p>
1575       * An array or collection is valid if its size is greater than, or equal to, the value of this keyword.
1576       *
1577       * <p>
1578       * Only allowed for the following types: <js>"array"</js>.
1579       *
1580       * <p>
1581       * Applicable to the following Swagger schema objects:
1582       * <ul>
1583       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1584       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1585       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1586       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1587       * </ul>
1588       *
1589       * @param value
1590       *    The new value for this property.
1591       *    <br>Ignored if value is <jk>null</jk> or <c>-1</c>.
1592       * @return This object.
1593       */
1594      public Builder minItems(Long value) {
1595         minItems = resolve(value, minItems);
1596         return this;
1597      }
1598
1599      /**
1600       * <mk>minItems</mk> field.
1601       *
1602       * <p>
1603       * Same as {@link #minItems(Long)} but takes in a string number.
1604       *
1605       * @param value
1606       *    The new value for this property.
1607       *    <br>Ignored if value is <jk>null</jk> or empty.
1608       * @return This object.
1609       */
1610      public Builder minItems(String value) {
1611         minItems = resolve(value, minItems);
1612         return this;
1613      }
1614
1615      /**
1616       * Synonym for {@link #minLength(Long)}.
1617       *
1618       * @param value
1619       *    The new value for this property.
1620       * @return This object.
1621       */
1622      public Builder minl(Long value) {
1623         return minLength(value);
1624      }
1625
1626      /**
1627       * Synonym for {@link #minLength(String)}.
1628       *
1629       * @param value
1630       *    The new value for this property.
1631       * @return This object.
1632       */
1633      public Builder minl(String value) {
1634         return minLength(value);
1635      }
1636
1637      /**
1638       * <mk>minLength</mk> field.
1639       *
1640       * <p>
1641       * A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
1642       * <br>The length of a string instance is defined as the number of its characters as defined by <a href='https://tools.ietf.org/html/rfc4627'>RFC 4627</a>.
1643       *
1644       * <p>
1645       * Only allowed for the following types: <js>"string"</js>.
1646       *
1647       * <p>
1648       * Applicable to the following Swagger schema objects:
1649       * <ul>
1650       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1651       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1652       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1653       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1654       * </ul>
1655       *
1656       * @param value
1657       *    The new value for this property.
1658       *    <br>Ignored if value is <jk>null</jk> or <c>-1</c>.
1659       * @return This object.
1660       */
1661      public Builder minLength(Long value) {
1662         minLength = resolve(value, minLength);
1663         return this;
1664      }
1665
1666      /**
1667       * <mk>minLength</mk> field.
1668       *
1669       * <p>
1670       * Same as {@link #minLength(Long)} but takes in a string number.
1671       *
1672       * @param value
1673       *    The new value for this property.
1674       *    <br>Ignored if value is <jk>null</jk> or empty.
1675       * @return This object.
1676       */
1677      public Builder minLength(String value) {
1678         minLength = resolve(value, minLength);
1679         return this;
1680      }
1681
1682      /**
1683       * Synonym for {@link #minProperties(Long)}.
1684       *
1685       * @param value
1686       *    The new value for this property.
1687       * @return This object.
1688       */
1689      public Builder minp(Long value) {
1690         return minProperties(value);
1691      }
1692
1693      /**
1694       * Synonym for {@link #minProperties(String)}.
1695       *
1696       * @param value
1697       *    The new value for this property.
1698       * @return This object.
1699       */
1700      public Builder minp(String value) {
1701         return minProperties(value);
1702      }
1703
1704      /**
1705       * <mk>minProperties</mk> field.
1706       *
1707       * <p>
1708       * Applicable to the following Swagger schema objects:
1709       * <ul>
1710       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1711       * </ul>
1712       *
1713       * @param value
1714       *    The new value for this property.
1715       *    <br>Ignored if value is <jk>null</jk>.
1716       * @return This object.
1717       */
1718      public Builder minProperties(Long value) {
1719         minProperties = resolve(value, minProperties);
1720         return this;
1721      }
1722
1723      /**
1724       * <mk>minProperties</mk> field.
1725       *
1726       * <p>
1727       * Same as {@link #minProperties(Long)} but takes in a string boolean.
1728       *
1729       * @param value
1730       *    The new value for this property.
1731       *    <br>Ignored if value is <jk>null</jk> or empty.
1732       * @return This object.
1733       */
1734      public Builder minProperties(String value) {
1735         minProperties = resolve(value, minProperties);
1736         return this;
1737      }
1738
1739      /**
1740       * Synonym for {@link #multipleOf(Number)}.
1741       *
1742       * @param value
1743       *    The new value for this property.
1744       * @return This object.
1745       */
1746      public Builder mo(Number value) {
1747         return multipleOf(value);
1748      }
1749
1750      /**
1751       * <mk>multipleOf</mk> field.
1752       *
1753       * <p>
1754       * A numeric instance is valid if the result of the division of the instance by this keyword's value is an integer.
1755       *
1756       * <p>
1757       * Only allowed for the following types: <js>"integer"</js>, <js>"number"</js>.
1758       *
1759       * <p>
1760       * Applicable to the following Swagger schema objects:
1761       * <ul>
1762       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1763       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1764       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1765       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1766       * </ul>
1767       *
1768       * @param value
1769       *    The new value for this property.
1770       *    <br>Ignored if value is <jk>null</jk>.
1771       * @return This object.
1772       */
1773      public Builder multipleOf(Number value) {
1774         if (nn(value))
1775            multipleOf = value;
1776         return this;
1777      }
1778
1779      /**
1780       * Synonym for {@link #name(String)}.
1781       *
1782       * @param value
1783       *    The new value for this property.
1784       * @return This object.
1785       */
1786      public Builder n(String value) {
1787         return name(value);
1788      }
1789
1790      /**
1791       * <mk>name</mk> field.
1792       *
1793       * <p>
1794       * Applicable to the following Swagger schema objects:
1795       * <ul>
1796       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1797       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1798       * </ul>
1799       *
1800       * @param value
1801       *    The new value for this property.
1802       * @return This object.
1803       */
1804      public Builder name(String value) {
1805         if (ne(value))
1806            name = value;
1807         return this;
1808      }
1809
1810      /**
1811       * Disables Swagger schema usage validation checking.
1812       *
1813       * <p>
1814       * Shortcut for calling <code>noValidate(<jk>true</jk>);</code>.
1815       *
1816       * @return This object.
1817       */
1818      public Builder noValidate() {
1819         return noValidate(true);
1820      }
1821
1822      /**
1823       * Disables Swagger schema usage validation checking.
1824       *
1825       * @param value Specify <jk>true</jk> to prevent {@link ContextRuntimeException} from being thrown if invalid Swagger usage was detected.
1826       * @return This object.
1827       */
1828      public Builder noValidate(Boolean value) {
1829         if (nn(value))
1830            noValidate = value;
1831         return this;
1832      }
1833
1834      /**
1835       * Synonym for {@link #pattern(String)}.
1836       *
1837       * @param value
1838       *    The new value for this property.
1839       * @return This object.
1840       */
1841      public Builder p(String value) {
1842         return pattern(value);
1843      }
1844
1845      /**
1846       * Shortcut for <c>property(key, value)</c>.
1847       *
1848       * <p>
1849       * Applicable to the following Swagger schema objects:
1850       * <ul>
1851       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1852       * </ul>
1853       *
1854       * @param key
1855       * The property name.
1856       * @param value
1857       *    The new value for this property.
1858       *    <br>Ignored if value is <jk>null</jk>.
1859       * @return This object.
1860       */
1861      public Builder p(String key, Builder value) {
1862         return property(key, value);
1863      }
1864
1865      /**
1866       * Shortcut for <c>property(key, value)</c>.
1867       *
1868       * <p>
1869       * Applicable to the following Swagger schema objects:
1870       * <ul>
1871       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1872       * </ul>
1873       *
1874       * @param key
1875       * The property name.
1876       * @param value
1877       *    The new value for this property.
1878       *    <br>Ignored if value is <jk>null</jk>.
1879       * @return This object.
1880       */
1881      public Builder p(String key, HttpPartSchema value) {
1882         return property(key, value);
1883      }
1884
1885      /**
1886       * Identifies the part parser to use for parsing this part.
1887       *
1888       * @param value
1889       *    The new value for this property.
1890       *    <br>Ignored if value is <jk>null</jk> or {@link HttpPartParser.Void}.
1891       * @return This object.
1892       */
1893      public Builder parser(Class<? extends HttpPartParser> value) {
1894         if (isNotVoid(value))
1895            parser = value;
1896         return this;
1897      }
1898
1899      /**
1900       * <mk>pattern</mk> field.
1901       *
1902       * <p>
1903       * A string input is valid if it matches the specified regular expression pattern.
1904       *
1905       * <p>
1906       * Only allowed for the following types: <js>"string"</js>.
1907       *
1908       * <p>
1909       * Applicable to the following Swagger schema objects:
1910       * <ul>
1911       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
1912       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1913       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
1914       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
1915       * </ul>
1916       *
1917       * @param value
1918       *    The new value for this property.
1919       *    <br>Ignored if value is <jk>null</jk> or empty.
1920       * @return This object.
1921       */
1922      public Builder pattern(String value) {
1923         try {
1924            if (ne(value))
1925               this.pattern = Pattern.compile(value);
1926         } catch (Exception e) {
1927            throw new ContextRuntimeException(e, "Invalid value {0} passed in as pattern value.  Must be a valid regular expression.", value);
1928         }
1929         return this;
1930      }
1931
1932      /**
1933       * <mk>properties</mk> field.
1934       *
1935       * <p>
1936       * Applicable to the following Swagger schema objects:
1937       * <ul>
1938       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1939       * </ul>
1940       *
1941       * @param key
1942       * The property name.
1943       * @param value
1944       *    The new value for this property.
1945       *    <br>Ignored if value is <jk>null</jk>.
1946       * @return This object.
1947       */
1948      public Builder property(String key, Builder value) {
1949         if (nn(key) && nn(value)) {
1950            if (properties == null)
1951               properties = map();
1952            properties.put(key, value);
1953         }
1954         return this;
1955      }
1956
1957      /**
1958       * <mk>properties</mk> field.
1959       *
1960       * <p>
1961       * Applicable to the following Swagger schema objects:
1962       * <ul>
1963       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
1964       * </ul>
1965       *
1966       * @param key
1967       * The property name.
1968       * @param value
1969       *    The new value for this property.
1970       *    <br>Ignored if value is <jk>null</jk>.
1971       * @return This object.
1972       */
1973      public Builder property(String key, HttpPartSchema value) {
1974         if (nn(key) && nn(value)) {
1975            if (properties == null)
1976               properties = map();
1977            properties.put(key, value);
1978         }
1979         return this;
1980      }
1981
1982      /**
1983       * Synonym for {@link #required()}.
1984       *
1985       * @return This object.
1986       */
1987      public Builder r() {
1988         return required();
1989      }
1990
1991      /**
1992       * Synonym for {@link #required(Boolean)}.
1993       *
1994       * @param value
1995       *    The new value for this property.
1996       * @return This object.
1997       */
1998      public Builder r(Boolean value) {
1999         return required(value);
2000      }
2001
2002      /**
2003       * Synonym for {@link #required(String)}.
2004       *
2005       * @param value
2006       *    The new value for this property.
2007       * @return This object.
2008       */
2009      public Builder r(String value) {
2010         return required(value);
2011      }
2012
2013      /**
2014       * <mk>required</mk> field.
2015       *
2016       * <p>
2017       * Shortcut for calling <code>required(<jk>true</jk>);</code>.
2018       *
2019       * @return This object.
2020       */
2021      public Builder required() {
2022         return required(true);
2023      }
2024
2025      /**
2026       * <mk>required</mk> field.
2027       *
2028       * <p>
2029       * Determines whether the parameter is mandatory.
2030       *
2031       * <p>
2032       * Applicable to the following Swagger schema objects:
2033       * <ul>
2034       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
2035       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
2036       * </ul>
2037       *
2038       * @param value
2039       *    The new value for this property.
2040       *    <br>Ignored if value is <jk>null</jk>.
2041       * @return This object.
2042       */
2043      public Builder required(Boolean value) {
2044         required = resolve(value, required);
2045         return this;
2046      }
2047
2048      /**
2049       * <mk>required</mk> field.
2050       *
2051       * <p>
2052       * Determines whether the parameter is mandatory.
2053       *
2054       * <p>
2055       * Same as {@link #required(Boolean)} but takes in a boolean value as a string.
2056       *
2057       * @param value
2058       *    The new value for this property.
2059       *    <br>Ignored if value is <jk>null</jk> or empty.
2060       * @return This object.
2061       */
2062      public Builder required(String value) {
2063         required = resolve(value, required);
2064         return this;
2065      }
2066
2067      /**
2068       * Identifies the part serializer to use for serializing this part.
2069       *
2070       * @param value
2071       *    The new value for this property.
2072       *    <br>Ignored if value is <jk>null</jk> or {@link HttpPartSerializer.Void}.
2073       * @return This object.
2074       */
2075      public Builder serializer(Class<? extends HttpPartSerializer> value) {
2076         if (isNotVoid(value))
2077            serializer = value;
2078         return this;
2079      }
2080
2081      /**
2082       * Synonym for {@link #skipIfEmpty()}.
2083       *
2084       * @return This object.
2085       */
2086      public Builder sie() {
2087         return skipIfEmpty();
2088      }
2089
2090      /**
2091       * Synonym for {@link #skipIfEmpty(Boolean)}.
2092       *
2093       * @param value
2094       *    The new value for this property.
2095       * @return This object.
2096       */
2097      public Builder sie(Boolean value) {
2098         return skipIfEmpty(value);
2099      }
2100
2101      /**
2102       * Synonym for {@link #skipIfEmpty(String)}.
2103       *
2104       * @param value
2105       *    The new value for this property.
2106       * @return This object.
2107       */
2108      public Builder sie(String value) {
2109         return skipIfEmpty(value);
2110      }
2111
2112      /**
2113       * Identifies whether an item should be skipped if it's empty.
2114       *
2115       * <p>
2116       * Shortcut for calling <code>skipIfEmpty(<jk>true</jk>);</code>.
2117       *
2118       * @return This object.
2119       */
2120      public Builder skipIfEmpty() {
2121         return skipIfEmpty(true);
2122      }
2123
2124      /**
2125       * <mk>skipIfEmpty</mk> field.
2126       *
2127       * <p>
2128       * Identifies whether an item should be skipped during serialization if it's empty.
2129       *
2130       * @param value
2131       *    The new value for this property.
2132       *    <br>Ignored if value is <jk>null</jk>.
2133       * @return This object.
2134       */
2135      public Builder skipIfEmpty(Boolean value) {
2136         skipIfEmpty = resolve(value, skipIfEmpty);
2137         return this;
2138      }
2139
2140      /**
2141       * <mk>skipIfEmpty</mk> field.
2142       *
2143       * <p>
2144       * Same as {@link #skipIfEmpty(Boolean)} but takes in a string boolean.
2145       *
2146       * @param value
2147       *    The new value for this property.
2148       *    <br>Ignored if value is <jk>null</jk> or empty.
2149       * @return This object.
2150       */
2151      public Builder skipIfEmpty(String value) {
2152         skipIfEmpty = resolve(value, skipIfEmpty);
2153         return this;
2154      }
2155
2156      /**
2157       * Synonym for {@link #type(HttpPartDataType)}.
2158       *
2159       * @param value
2160       *    The new value for this property.
2161       * @return This object.
2162       */
2163      public Builder t(HttpPartDataType value) {
2164         return type(value);
2165      }
2166
2167      /**
2168       * Synonym for {@link #type(String)}.
2169       *
2170       * @param value
2171       *    The new value for this property.
2172       * @return This object.
2173       */
2174      public Builder t(String value) {
2175         return type(value);
2176      }
2177
2178      /**
2179       * Shortcut for <c>type(HttpPartDataType.ARRAY)</c>.
2180       *
2181       * @return This object.
2182       */
2183      public Builder tArray() {
2184         type = HttpPartDataType.ARRAY;
2185         return this;
2186      }
2187
2188      /**
2189       * Shortcut for <c>type(HttpPartDataType.BOOLEAN)</c>.
2190       *
2191       * @return This object.
2192       */
2193      public Builder tBoolean() {
2194         type = HttpPartDataType.BOOLEAN;
2195         return this;
2196      }
2197
2198      /**
2199       * Shortcut for <c>type(HttpPartDataType.FILE)</c>.
2200       *
2201       * @return This object.
2202       */
2203      public Builder tFile() {
2204         type = HttpPartDataType.FILE;
2205         return this;
2206      }
2207
2208      /**
2209       * Shortcut for <c>type(HttpPartDataType.INTEGER)</c>.
2210       *
2211       * @return This object.
2212       */
2213      public Builder tInteger() {
2214         type = HttpPartDataType.INTEGER;
2215         return this;
2216      }
2217
2218      /**
2219       * Shortcut for <c>type(HttpPartDataType.NO_TYPE)</c>.
2220       *
2221       * @return This object.
2222       */
2223      public Builder tNone() {
2224         type = HttpPartDataType.NO_TYPE;
2225         return this;
2226      }
2227
2228      /**
2229       * Shortcut for <c>type(HttpPartDataType.NUMBER)</c>.
2230       *
2231       * @return This object.
2232       */
2233      public Builder tNumber() {
2234         type = HttpPartDataType.NUMBER;
2235         return this;
2236      }
2237
2238      /**
2239       * Shortcut for <c>type(HttpPartDataType.OBJECT)</c>.
2240       *
2241       * @return This object.
2242       */
2243      public Builder tObject() {
2244         type = HttpPartDataType.OBJECT;
2245         return this;
2246      }
2247
2248      /**
2249       * Shortcut for <c>type(HttpPartDataType.STRING)</c>.
2250       *
2251       * @return This object.
2252       */
2253      public Builder tString() {
2254         type = HttpPartDataType.STRING;
2255         return this;
2256      }
2257
2258      /**
2259       * <mk>type</mk> field.
2260       *
2261       * <p>
2262       * The type of the parameter.
2263       *
2264       * <p>
2265       * If the type is not specified, it will be auto-detected based on the parameter class type.
2266       *
2267       * <p>
2268       * Applicable to the following Swagger schema objects:
2269       * <ul>
2270       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
2271       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
2272       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
2273       *    <li><a class="doclink" href="https://swagger.io/specification/v2#securitySchemeObject">SecurityScheme</a>
2274       * </ul>
2275       *
2276       * <ul class='values javatree'>
2277       *    <li class='jc'>{@link HttpPartDataType}
2278       *    <ul>
2279       *       <li class='jf'>
2280       *          {@link HttpPartDataType#STRING STRING}
2281       *          <br>Parameter must be a string or a POJO convertible from a string.
2282       *          <li>
2283       *          {@link HttpPartDataType#NUMBER NUMBER}
2284       *          <br>Parameter must be a number primitive or number object.
2285       *          <br>If parameter is <c>Object</c>, creates either a <c>Float</c> or <c>Double</c> depending on the size of the number.
2286       *       <li class='jf'>
2287       *          {@link HttpPartDataType#INTEGER INTEGER}
2288       *          <br>Parameter must be a integer/long primitive or integer/long object.
2289       *          <br>If parameter is <c>Object</c>, creates either a <c>Short</c>, <c>Integer</c>, or <c>Long</c> depending on the size of the number.
2290       *       <li class='jf'>
2291       *          {@link HttpPartDataType#BOOLEAN BOOLEAN}
2292       *          <br>Parameter must be a boolean primitive or object.
2293       *       <li class='jf'>
2294       *          {@link HttpPartDataType#ARRAY ARRAY}
2295       *          <br>Parameter must be an array or collection.
2296       *          <br>Elements must be strings or POJOs convertible from strings.
2297       *          <br>If parameter is <c>Object</c>, creates an {@link JsonList}.
2298       *       <li class='jf'>
2299       *          {@link HttpPartDataType#OBJECT OBJECT}
2300       *          <br>Parameter must be a map or bean.
2301       *          <br>If parameter is <c>Object</c>, creates an {@link JsonMap}.
2302       *          <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
2303       *       <li class='jf'>
2304       *          {@link HttpPartDataType#FILE FILE}
2305       *          <br>This type is currently not supported.
2306       *    </ul>
2307       * </ul>
2308       *
2309       * <h5 class='section'>See Also:</h5><ul>
2310       *    <li class='extlink'><a class="doclink" href="https://swagger.io/specification#dataTypes">Swagger Data Types</a>
2311       * </ul>
2312       *
2313       * @param value
2314       *    The new value for this property.
2315       * @return This object.
2316       */
2317      public Builder type(HttpPartDataType value) {
2318         type = value;
2319         return this;
2320      }
2321
2322      /**
2323       * <mk>type</mk> field.
2324       *
2325       * <p>
2326       * The type of the parameter.
2327       *
2328       * <p>
2329       * If the type is not specified, it will be auto-detected based on the parameter class type.
2330       *
2331       * <p>
2332       * Applicable to the following Swagger schema objects:
2333       * <ul>
2334       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
2335       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
2336       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
2337       *    <li><a class="doclink" href="https://swagger.io/specification/v2#securitySchemeObject">SecurityScheme</a>
2338       * </ul>
2339       *
2340       * <ul class='values'>
2341       *    <li>
2342       *       <js>"string"</js>
2343       *       <br>Parameter must be a string or a POJO convertible from a string.
2344       *    <li>
2345       *       <js>"number"</js>
2346       *       <br>Parameter must be a number primitive or number object.
2347       *       <br>If parameter is <c>Object</c>, creates either a <c>Float</c> or <c>Double</c> depending on the size of the number.
2348       *    <li>
2349       *       <js>"integer"</js>
2350       *       <br>Parameter must be a integer/long primitive or integer/long object.
2351       *       <br>If parameter is <c>Object</c>, creates either a <c>Short</c>, <c>Integer</c>, or <c>Long</c> depending on the size of the number.
2352       *    <li>
2353       *       <js>"boolean"</js>
2354       *       <br>Parameter must be a boolean primitive or object.
2355       *    <li>
2356       *       <js>"array"</js>
2357       *       <br>Parameter must be an array or collection.
2358       *       <br>Elements must be strings or POJOs convertible from strings.
2359       *       <br>If parameter is <c>Object</c>, creates an {@link JsonList}.
2360       *    <li>
2361       *       <js>"object"</js>
2362       *       <br>Parameter must be a map or bean.
2363       *       <br>If parameter is <c>Object</c>, creates an {@link JsonMap}.
2364       *       <br>Note that this is an extension of the OpenAPI schema as Juneau allows for arbitrarily-complex POJOs to be serialized as HTTP parts.
2365       *    <li>
2366       *       <js>"file"</js>
2367       *       <br>This type is currently not supported.
2368       * </ul>
2369       *
2370       * <h5 class='section'>See Also:</h5><ul>
2371       *    <li class='extlink'><a class="doclink" href="https://swagger.io/specification#dataTypes">Swagger Data Types</a>
2372       * </ul>
2373       *
2374       * @param value
2375       *    The new value for this property.
2376       *    <br>Ignored if value is <jk>null</jk> or empty.
2377       * @return This object.
2378       */
2379      public Builder type(String value) {
2380         try {
2381            if (ne(value))
2382               type = HttpPartDataType.fromString(value);
2383         } catch (Exception e) {
2384            throw new ContextRuntimeException(e, "Invalid value ''{0}'' passed in as type value.  Valid values: {1}", value, HttpPartDataType.values());
2385         }
2386         return this;
2387      }
2388
2389      /**
2390       * Synonym for {@link #uniqueItems()}.
2391       *
2392       * @return This object.
2393       */
2394      public Builder ui() {
2395         return uniqueItems();
2396      }
2397
2398      /**
2399       * Synonym for {@link #uniqueItems(Boolean)}.
2400       *
2401       * @param value
2402       *    The new value for this property.
2403       * @return This object.
2404       */
2405      public Builder ui(Boolean value) {
2406         return uniqueItems(value);
2407      }
2408
2409      /**
2410       * Synonym for {@link #uniqueItems(String)}.
2411       *
2412       * @param value
2413       *    The new value for this property.
2414       * @return This object.
2415       */
2416      public Builder ui(String value) {
2417         return uniqueItems(value);
2418      }
2419
2420      /**
2421       * <mk>uniqueItems</mk> field.
2422       *
2423       * <p>
2424       * Shortcut for calling <code>uniqueItems(<jk>true</jk>);</code>.
2425       *
2426       * @return This object.
2427       */
2428      public Builder uniqueItems() {
2429         return uniqueItems(true);
2430      }
2431
2432      /**
2433       * <mk>uniqueItems</mk> field.
2434       *
2435       * <p>
2436       * If <jk>true</jk>, the input validates successfully if all of its elements are unique.
2437       *
2438       * <p>
2439       * <br>If the parameter type is a subclass of {@link Set}, this validation is skipped (since a set can only contain unique items anyway).
2440       * <br>Otherwise, the collection or array is checked for duplicate items.
2441       *
2442       * <p>
2443       * Only allowed for the following types: <js>"array"</js>.
2444       *
2445       * <p>
2446       * Applicable to the following Swagger schema objects:
2447       * <ul>
2448       *    <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a>
2449       *    <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a>
2450       *    <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a>
2451       *    <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a>
2452       * </ul>
2453       *
2454       * @param value
2455       *    The new value for this property.
2456       *    <br>Ignored if value is <jk>null</jk>.
2457       * @return This object.
2458       */
2459      public Builder uniqueItems(Boolean value) {
2460         uniqueItems = resolve(value, uniqueItems);
2461         return this;
2462      }
2463
2464      /**
2465       * <mk>uniqueItems</mk> field.
2466       *
2467       * <p>
2468       * Same as {@link #uniqueItems(Boolean)} but takes in a string boolean.
2469       *
2470       * @param value
2471       *    The new value for this property.
2472       *    <br>Ignored if value is <jk>null</jk> or empty..
2473       * @return This object.
2474       */
2475      public Builder uniqueItems(String value) {
2476         uniqueItems = resolve(value, uniqueItems);
2477         return this;
2478      }
2479
2480      private Builder additionalProperties(JsonMap value) {
2481         if (nn(value) && ! value.isEmpty())
2482            additionalProperties = HttpPartSchema.create().apply(value);
2483         return this;
2484      }
2485
2486      private static Long firstNmo(Long...l) {
2487         for (var ll : l)
2488            if (nn(ll) && ll != -1)
2489               return ll;
2490         return null;
2491      }
2492
2493      /**
2494       * Helper method to safely get annotation attribute values via reflection.
2495       *
2496       * <p>
2497       * This allows reading Jakarta Validation annotations without requiring them on the classpath.
2498       *
2499       * @param <T> The expected return type.
2500       * @param a The annotation to read from.
2501       * @param attributeName The attribute name to read.
2502       * @param type The expected type of the attribute value.
2503       * @return The attribute value, or <jk>null</jk> if not found or not of the expected type.
2504       */
2505      private static <T> T getAnnotationValue(Annotation a, String attributeName, Class<T> type) {
2506         try {
2507            Method m = a.annotationType().getDeclaredMethod(attributeName);
2508            Object value = m.invoke(a);
2509            return type.isInstance(value) ? type.cast(value) : null;
2510         } catch (@SuppressWarnings("unused") Exception e) {
2511            return null;
2512         }
2513      }
2514
2515      private static String joinnlOrNull(String[]...s) {
2516         for (var ss : s)
2517            if (ss.length > 0)
2518               return StringUtils.joinnl(ss);
2519         return null;
2520      }
2521
2522      private Builder properties(JsonMap value) {
2523         if (nn(value))
2524            value.forEach((k, v) -> property(k, HttpPartSchema.create().apply((JsonMap)v)));
2525         return this;
2526      }
2527
2528      private static Boolean resolve(Boolean newValue, Boolean oldValue) {
2529         return newValue == null ? oldValue : newValue;
2530      }
2531
2532      private static Long resolve(Long newValue, Long oldValue) {
2533         return (newValue == null || newValue == -1) ? oldValue : newValue;
2534      }
2535
2536      private static Boolean resolve(String newValue, Boolean oldValue) {
2537         return isEmpty(newValue) ? oldValue : bool(newValue);
2538      }
2539
2540      private static Long resolve(String newValue, Long oldValue) {
2541         return isEmpty(newValue) ? oldValue : Long.parseLong(newValue);
2542      }
2543
2544      private static Number toNumber(String...s) {
2545         return HttpPartSchema.toNumber(s);
2546      }
2547
2548      private static Set<String> toSet(String[]...s) {
2549         return HttpPartSchema.toSet(s);
2550      }
2551
2552      Builder apply(Class<? extends Annotation> c, java.lang.reflect.Type t) {
2553         if (t instanceof Class<?> c2) {
2554            rstream(AP.find(c, info(c2))).forEach(x -> apply(x.inner()));
2555         } else if (Value.isType(t)) {
2556            apply(c, Value.getParameterType(t));
2557         }
2558         return this;
2559      }
2560
2561      Builder apply(Class<? extends Annotation> c, Method m) {
2562         apply(c, m.getGenericReturnType());
2563         Annotation a = m.getAnnotation(c);
2564         if (nn(a))
2565            return apply(a);
2566         return this;
2567      }
2568
2569      Builder apply(Class<? extends Annotation> c, ParameterInfo mpi) {
2570         apply(c, mpi.getParameterType().innerType());
2571         mpi.getAnnotations(c).forEach(x -> apply(x.inner()));
2572         return this;
2573      }
2574
2575      Builder apply(Content a) {
2576         if (! SchemaAnnotation.empty(a.schema()))
2577            apply(a.schema());
2578         default_(a.def());
2579         return this;
2580      }
2581
2582      Builder apply(FormData a) {
2583         if (! SchemaAnnotation.empty(a.schema()))
2584            apply(a.schema());
2585         name(firstNonEmpty(a.name(), a.value()));
2586         default_(a.def());
2587         parser(a.parser());
2588         serializer(a.serializer());
2589         return this;
2590      }
2591
2592      Builder apply(HasFormData a) {
2593         name(firstNonEmpty(a.name(), a.value()));
2594         return this;
2595      }
2596
2597      Builder apply(HasQuery a) {
2598         name(firstNonEmpty(a.name(), a.value()));
2599         return this;
2600      }
2601
2602      Builder apply(Header a) {
2603         if (! SchemaAnnotation.empty(a.schema()))
2604            apply(a.schema());
2605         name(firstNonEmpty(a.name(), a.value()));
2606         default_(a.def());
2607         parser(a.parser());
2608         serializer(a.serializer());
2609         return this;
2610      }
2611
2612      Builder apply(Items a) {
2613         default_(joinnlOrNull(a.default_(), a.df()));
2614         enum_(toSet(a.enum_(), a.e()));
2615         collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf()));
2616         exclusiveMaximum(a.exclusiveMaximum() || a.emax());
2617         exclusiveMinimum(a.exclusiveMinimum() || a.emin());
2618         format(firstNonEmpty(a.format(), a.f()));
2619         items(a.items());
2620         maximum(toNumber(a.maximum(), a.max()));
2621         maxItems(firstNmo(a.maxItems(), a.maxi()));
2622         maxLength(firstNmo(a.maxLength(), a.maxl()));
2623         minimum(toNumber(a.minimum(), a.min()));
2624         minItems(firstNmo(a.minItems(), a.mini()));
2625         minLength(firstNmo(a.minLength(), a.minl()));
2626         multipleOf(toNumber(a.multipleOf(), a.mo()));
2627         pattern(firstNonEmpty(a.pattern(), a.p()));
2628         type(firstNonEmpty(a.type(), a.t()));
2629         uniqueItems(a.uniqueItems() || a.ui());
2630         return this;
2631      }
2632
2633      // -----------------------------------------------------------------------------------------------------------------
2634      // JSON Schema Draft 2020-12 property setters
2635      // -----------------------------------------------------------------------------------------------------------------
2636
2637      Builder apply(JsonMap m) {
2638         if (nn(m) && ! m.isEmpty()) {
2639            default_(m.getString("default"));
2640            enum_(HttpPartSchema.toSet(m.getString("enum")));
2641            allowEmptyValue(m.getBoolean("allowEmptyValue"));
2642            exclusiveMaximum(m.getBoolean("exclusiveMaximum"));
2643            exclusiveMinimum(m.getBoolean("exclusiveMinimum"));
2644            required(m.getBoolean("required"));
2645            uniqueItems(m.getBoolean("uniqueItems"));
2646            collectionFormat(m.getString("collectionFormat"));
2647            type(m.getString("type"));
2648            format(m.getString("format"));
2649            pattern(m.getString("pattern"));
2650            maximum(m.get("maximum", Number.class));
2651            minimum(m.get("minimum", Number.class));
2652            multipleOf(m.get("multipleOf", Number.class));
2653            maxItems(m.get("maxItems", Long.class));
2654            maxLength(m.get("maxLength", Long.class));
2655            maxProperties(m.get("maxProperties", Long.class));
2656            minItems(m.get("minItems", Long.class));
2657            minLength(m.get("minLength", Long.class));
2658            minProperties(m.get("minProperties", Long.class));
2659
2660            items(m.getMap("items"));
2661            properties(m.getMap("properties"));
2662            additionalProperties(m.getMap("additionalProperties"));
2663
2664            apply(m.getMap("schema", null));
2665         }
2666         return this;
2667      }
2668
2669      Builder apply(Path a) {
2670         if (! SchemaAnnotation.empty(a.schema()))
2671            apply(a.schema());
2672         name(firstNonEmpty(a.name(), a.value()));
2673         String def = a.def();
2674         if (neq(NONE, def))
2675            default_ = def;  // Set directly to allow empty strings as valid defaults
2676         parser(a.parser());
2677         serializer(a.serializer());
2678
2679         // Path remainder always allows empty value.
2680         if (startsWith(name, '/')) {
2681            allowEmptyValue();
2682            required(false);
2683         } else if (required == null) {
2684            // Path parameters with default values are not required
2685            required(eq(NONE, def));
2686         }
2687
2688         return this;
2689      }
2690
2691      Builder apply(PathRemainder a) {
2692         if (! SchemaAnnotation.empty(a.schema()))
2693            apply(a.schema());
2694         // PathRemainder is always "/*"
2695         name("/*");
2696         default_(a.def());
2697         parser(a.parser());
2698         serializer(a.serializer());
2699
2700         // Path remainder always allows empty value.
2701         allowEmptyValue();
2702         required(false);
2703
2704         return this;
2705      }
2706
2707      Builder apply(Query a) {
2708         if (! SchemaAnnotation.empty(a.schema()))
2709            apply(a.schema());
2710         name(firstNonEmpty(a.name(), a.value()));
2711         default_(a.def());
2712         parser(a.parser());
2713         serializer(a.serializer());
2714         return this;
2715      }
2716
2717      Builder apply(Response a) {
2718         allowEmptyValue(true);
2719         apply(a.schema());
2720         parser(a.parser());
2721         required(false);
2722         serializer(a.serializer());
2723         return this;
2724      }
2725
2726      // -----------------------------------------------------------------------------------------------------------------
2727      // Other
2728      // -----------------------------------------------------------------------------------------------------------------
2729
2730      @SuppressWarnings("deprecation")
2731      Builder apply(Schema a) {
2732         default_(joinnlOrNull(a.default_(), a.df()));
2733         enum_(toSet(a.enum_(), a.e()));
2734         additionalProperties(HttpPartSchema.toJsonMap(a.additionalProperties()));
2735         allowEmptyValue(a.allowEmptyValue() || a.aev());
2736         collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf()));
2737
2738         // Handle exclusiveMaximum with fallback from Draft 2020-12 to Draft 04
2739         String exMaxVal = a.exclusiveMaximumValue();
2740         if (ne(exMaxVal)) {
2741            exclusiveMaximumValue(toNumber(exMaxVal));
2742         } else if (a.exclusiveMaximum() || a.emax()) {
2743            exclusiveMaximum(true);
2744         }
2745
2746         // Handle exclusiveMinimum with fallback from Draft 2020-12 to Draft 04
2747         String exMinVal = a.exclusiveMinimumValue();
2748         if (ne(exMinVal)) {
2749            exclusiveMinimumValue(toNumber(exMinVal));
2750         } else if (a.exclusiveMinimum() || a.emin()) {
2751            exclusiveMinimum(true);
2752         }
2753
2754         format(firstNonEmpty(a.format(), a.f()));
2755         items(a.items());
2756         maximum(toNumber(a.maximum(), a.max()));
2757         maxItems(firstNmo(a.maxItems(), a.maxi()));
2758         maxLength(firstNmo(a.maxLength(), a.maxl()));
2759         maxProperties(firstNmo(a.maxProperties(), a.maxp()));
2760         minimum(toNumber(a.minimum(), a.min()));
2761         minItems(firstNmo(a.minItems(), a.mini()));
2762         minLength(firstNmo(a.minLength(), a.minl()));
2763         minProperties(firstNmo(a.minProperties(), a.minp()));
2764         multipleOf(toNumber(a.multipleOf(), a.mo()));
2765         pattern(firstNonEmpty(a.pattern(), a.p()));
2766         properties(HttpPartSchema.toJsonMap(a.properties()));
2767         required(a.required() || a.r());
2768         skipIfEmpty(a.skipIfEmpty() || a.sie());
2769         type(firstNonEmpty(a.type(), a.t()));
2770         uniqueItems(a.uniqueItems() || a.ui());
2771
2772         // JSON Schema Draft 2020-12 properties
2773         const_(joinnlOrNull(a.const_()));
2774         examples(a.examples());
2775         deprecated(a.deprecatedProperty());
2776
2777         return this;
2778      }
2779
2780      Builder apply(StatusCode a) {
2781         codes(a.value());
2782         return this;
2783      }
2784
2785      Builder apply(SubItems a) {
2786         default_(joinnlOrNull(a.default_(), a.df()));
2787         enum_(toSet(a.enum_(), a.e()));
2788         collectionFormat(firstNonEmpty(a.collectionFormat(), a.cf()));
2789         exclusiveMaximum(a.exclusiveMaximum() || a.emax());
2790         exclusiveMinimum(a.exclusiveMinimum() || a.emin());
2791         format(firstNonEmpty(a.format(), a.f()));
2792         items(HttpPartSchema.toJsonMap(a.items()));
2793         maximum(toNumber(a.maximum(), a.max()));
2794         maxItems(firstNmo(a.maxItems(), a.maxi()));
2795         maxLength(firstNmo(a.maxLength(), a.maxl()));
2796         minimum(toNumber(a.minimum(), a.min()));
2797         minItems(firstNmo(a.minItems(), a.mini()));
2798         minLength(firstNmo(a.minLength(), a.minl()));
2799         multipleOf(toNumber(a.multipleOf(), a.mo()));
2800         pattern(firstNonEmpty(a.pattern(), a.p()));
2801         type(firstNonEmpty(a.type(), a.t()));
2802         uniqueItems(a.uniqueItems() || a.ui());
2803         return this;
2804      }
2805
2806      Builder applyAll(Class<? extends Annotation> c, java.lang.reflect.Type t) {
2807         return apply(Schema.class, t).apply(c, t);
2808      }
2809
2810      Builder applyAll(Class<? extends Annotation> c, Method m) {
2811         return apply(Schema.class, m).apply(c, m);
2812      }
2813
2814      Builder applyAll(Class<? extends Annotation> c, ParameterInfo mpi) {
2815         return apply(Schema.class, mpi).apply(c, mpi);
2816      }
2817
2818      /**
2819       * Apply Jakarta Bean Validation constraints to this schema.
2820       *
2821       * <p>
2822       * This method uses pure reflection to read constraint annotations, so no jakarta.validation-api
2823       * dependency is required. The constraints are mapped to OpenAPI schema properties where applicable.
2824       *
2825       * <p>
2826       * Supported constraints:
2827       * <ul>
2828       *    <li><c>@NotNull</c> → <c>required(true)</c>
2829       *    <li><c>@Size</c> → <c>minLength/maxLength</c> or <c>minItems/maxItems</c>
2830       *    <li><c>@Min</c> → <c>minimum</c>
2831       *    <li><c>@Max</c> → <c>maximum</c>
2832       *    <li><c>@DecimalMin</c> → <c>minimum + exclusiveMinimum</c>
2833       *    <li><c>@DecimalMax</c> → <c>maximum + exclusiveMaximum</c>
2834       *    <li><c>@Pattern</c> → <c>pattern</c>
2835       *    <li><c>@Email</c> → <c>format("email")</c>
2836       *    <li><c>@Positive</c> → <c>minimum(0) + exclusiveMinimum(true)</c>
2837       *    <li><c>@PositiveOrZero</c> → <c>minimum(0)</c>
2838       *    <li><c>@Negative</c> → <c>maximum(0) + exclusiveMaximum(true)</c>
2839       *    <li><c>@NegativeOrZero</c> → <c>maximum(0)</c>
2840       *    <li><c>@NotEmpty</c> → <c>required(true) + minLength(1)/minItems(1)</c>
2841       *    <li><c>@NotBlank</c> → <c>required(true) + minLength(1) + pattern</c>
2842       * </ul>
2843       *
2844       * @param a The Jakarta Validation constraint annotation.
2845       * @return This object.
2846       * @since 9.2.0
2847       */
2848      Builder applyJakartaValidation(Annotation a) {
2849         String simpleName = cns(a.annotationType());
2850
2851         try {
2852            switch (simpleName) {
2853               case "NotNull":
2854                  required(true);
2855                  break;
2856               case "Size":
2857                  Integer min = getAnnotationValue(a, "min", Integer.class);
2858                  Integer max = getAnnotationValue(a, "max", Integer.class);
2859                  if (nn(min) && min > 0) {
2860                     minLength(min.longValue());
2861                     minItems(min.longValue());
2862                  }
2863                  if (nn(max) && max < Integer.MAX_VALUE) {
2864                     maxLength(max.longValue());
2865                     maxItems(max.longValue());
2866                  }
2867                  break;
2868               case "Min":
2869                  Long minValue = getAnnotationValue(a, "value", Long.class);
2870                  if (nn(minValue))
2871                     minimum(minValue);
2872                  break;
2873               case "Max":
2874                  Long maxValue = getAnnotationValue(a, "value", Long.class);
2875                  if (nn(maxValue))
2876                     maximum(maxValue);
2877                  break;
2878               case "Pattern":
2879                  String regexp = getAnnotationValue(a, "regexp", String.class);
2880                  if (nn(regexp))
2881                     pattern(regexp);
2882                  break;
2883               case "Email":
2884                  format("email");
2885                  break;
2886               case "Positive":
2887                  minimum(0);
2888                  exclusiveMinimum(true);
2889                  break;
2890               case "PositiveOrZero":
2891                  minimum(0);
2892                  break;
2893               case "Negative":
2894                  maximum(0);
2895                  exclusiveMaximum(true);
2896                  break;
2897               case "NegativeOrZero":
2898                  maximum(0);
2899                  break;
2900               case "NotEmpty":
2901                  required(true);
2902                  minLength(1L);
2903                  minItems(1L);
2904                  break;
2905               case "NotBlank":
2906                  required(true);
2907                  minLength(1L);
2908                  pattern(".*\\S.*"); // Contains at least one non-whitespace character
2909                  break;
2910               case "DecimalMin":
2911                  String minVal = getAnnotationValue(a, "value", String.class);
2912                  Boolean minInclusive = getAnnotationValue(a, "inclusive", Boolean.class);
2913                  if (nn(minVal)) {
2914                     minimum(toNumber(minVal));
2915                     if (Boolean.FALSE.equals(minInclusive))
2916                        exclusiveMinimum(true);
2917                  }
2918                  break;
2919               case "DecimalMax":
2920                  String maxVal = getAnnotationValue(a, "value", String.class);
2921                  Boolean maxInclusive = getAnnotationValue(a, "inclusive", Boolean.class);
2922                  if (nn(maxVal)) {
2923                     maximum(toNumber(maxVal));
2924                     if (Boolean.FALSE.equals(maxInclusive))
2925                        exclusiveMaximum(true);
2926                  }
2927                  break;
2928               default:
2929                  // Silently ignore other validation annotations we don't support yet
2930            }
2931         } catch (@SuppressWarnings("unused") Exception e) {
2932            // If reflection fails, just skip this annotation - it's optional
2933         }
2934         return this;
2935      }
2936
2937      Builder items(Items value) {
2938         if (! ItemsAnnotation.empty(value))
2939            items = HttpPartSchema.create().apply(value);
2940         return this;
2941      }
2942
2943      Builder items(JsonMap value) {
2944         if (nn(value) && ! value.isEmpty())
2945            items = HttpPartSchema.create().apply(value);
2946         return this;
2947      }
2948
2949      Builder items(SubItems value) {
2950         if (! SubItemsAnnotation.empty(value))
2951            items = HttpPartSchema.create().apply(value);
2952         return this;
2953      }
2954   }
2955
2956   /** Reusable instance of this object, all default settings. */
2957   public static final HttpPartSchema DEFAULT = HttpPartSchema.create().allowEmptyValue(true).build();
2958
2959   /** Boolean type */
2960   public static final HttpPartSchema T_BOOLEAN = HttpPartSchema.tBoolean().build();
2961
2962   /** File type */
2963   public static final HttpPartSchema T_FILE = HttpPartSchema.tFile().build();
2964
2965   /** Integer type */
2966   public static final HttpPartSchema T_INTEGER = HttpPartSchema.tInteger().build();
2967
2968   /** Int32 type */
2969   public static final HttpPartSchema T_INT32 = HttpPartSchema.tInt32().build();
2970
2971   /** Int64 type */
2972   public static final HttpPartSchema T_INT64 = HttpPartSchema.tInt64().build();
2973
2974   /** No type */
2975   public static final HttpPartSchema T_NONE = HttpPartSchema.tNone().build();
2976
2977   /** Number type */
2978   public static final HttpPartSchema T_NUMBER = HttpPartSchema.tNumber().build();
2979
2980   /** Float type */
2981   public static final HttpPartSchema T_FLOAT = HttpPartSchema.tFloat().build();
2982
2983   /** Double type */
2984   public static final HttpPartSchema T_DOUBLE = HttpPartSchema.tDouble().build();
2985
2986   /** String type */
2987   public static final HttpPartSchema T_STRING = HttpPartSchema.tString().build();
2988
2989   /** Byte type */
2990   public static final HttpPartSchema T_BYTE = HttpPartSchema.tByte().build();
2991
2992   /** Binary type */
2993   public static final HttpPartSchema T_BINARY = HttpPartSchema.tBinary().build();
2994
2995   /** Spaced binary type */
2996   public static final HttpPartSchema T_BINARY_SPACED = HttpPartSchema.tBinarySpaced().build();
2997
2998   /** Date type */
2999   public static final HttpPartSchema T_DATE = HttpPartSchema.tDate().build();
3000
3001   /** Date-time type */
3002   public static final HttpPartSchema T_DATETIME = HttpPartSchema.tDateTime().build();
3003
3004   /** UON-formated simple type */
3005   public static final HttpPartSchema T_UON = HttpPartSchema.tUon().build();
3006
3007   /** Array type */
3008   public static final HttpPartSchema T_ARRAY = HttpPartSchema.tArray().build();
3009
3010   /** Comma-delimited array type */
3011   public static final HttpPartSchema T_ARRAY_CSV = HttpPartSchema.tArrayCsv().build();
3012
3013   /** Pipe-delimited array type */
3014   public static final HttpPartSchema T_ARRAY_PIPES = HttpPartSchema.tArrayPipes().build();
3015
3016   /** Space-delimited array type */
3017   public static final HttpPartSchema T_ARRAY_SSV = HttpPartSchema.tArraySsv().build();
3018
3019   /** Tab-delimited array type */
3020   public static final HttpPartSchema T_ARRAY_TSV = HttpPartSchema.tArrayTsv().build();
3021
3022   /** UON-formatted array type */
3023   public static final HttpPartSchema T_ARRAY_UON = HttpPartSchema.tArrayUon().build();
3024
3025   /** Multi-part array type */
3026   public static final HttpPartSchema T_ARRAY_MULTI = HttpPartSchema.tArrayMulti().build();
3027
3028   /** Object type */
3029   public static final HttpPartSchema T_OBJECT = HttpPartSchema.tObject().build();
3030
3031   /** Comma-delimited object type */
3032   public static final HttpPartSchema T_OBJECT_CSV = HttpPartSchema.tObjectCsv().build();
3033
3034   /** Pipe-delimited object type */
3035   public static final HttpPartSchema T_OBJECT_PIPES = HttpPartSchema.tObjectPipes().build();
3036
3037   /** Space-delimited object type */
3038   public static final HttpPartSchema T_OBJECT_SSV = HttpPartSchema.tObjectSsv().build();
3039
3040   /** Tab-delimited object type */
3041   public static final HttpPartSchema T_OBJECT_TSV = HttpPartSchema.tObjectTsv().build();
3042
3043   /** UON-formated object type */
3044   public static final HttpPartSchema T_OBJECT_UON = HttpPartSchema.tObjectUon().build();
3045
3046   /**
3047    * Instantiates a new builder for this object.
3048    *
3049    * @return A new builder for this object.
3050    */
3051   public static Builder create() {
3052      return new Builder();
3053   }
3054
3055   /**
3056    * Finds the schema information on the specified annotation.
3057    *
3058    * @param a
3059    *    The annotation to find the schema information on..
3060    * @return The schema information found on the annotation.
3061    */
3062   public static HttpPartSchema create(Annotation a) {
3063      return create().apply(a).build();
3064   }
3065
3066   /**
3067    * Finds the schema information on the specified annotation.
3068    *
3069    * @param a
3070    *    The annotation to find the schema information on..
3071    * @param defaultName The default part name if not specified on the annotation.
3072    * @return The schema information found on the annotation.
3073    */
3074   public static HttpPartSchema create(Annotation a, String defaultName) {
3075      return create().name(defaultName).apply(a).build();
3076   }
3077
3078   /**
3079    * Finds the schema information for the specified class.
3080    *
3081    * <p>
3082    * This method will gather all the schema information from the annotations on the class and all parent classes/interfaces.
3083    *
3084    * @param c
3085    *    The annotation to look for.
3086    *    <br>Valid values:
3087    *    <ul>
3088    *       <li>{@link Content}
3089    *       <li>{@link Header}
3090    *       <li>{@link Query}
3091    *       <li>{@link FormData}
3092    *       <li>{@link Path}
3093    *       <li>{@link Response}
3094    *       <li>{@link HasQuery}
3095    *       <li>{@link HasFormData}
3096    *    </ul>
3097    * @param t
3098    *    The class containing the parameter.
3099    * @return The schema information about the parameter.
3100    */
3101   public static HttpPartSchema create(Class<? extends Annotation> c, java.lang.reflect.Type t) {
3102      return create().applyAll(c, t).build();
3103   }
3104
3105   /**
3106    * Finds the schema information for the specified method return.
3107    *
3108    * <p>
3109    * This method will gather all the schema information from the annotations at the following locations:
3110    * <ul>
3111    *    <li>The method.
3112    *    <li>The method return class.
3113    *    <li>The method return parent classes and interfaces.
3114    * </ul>
3115    *
3116    * @param c
3117    *    The annotation to look for.
3118    *    <br>Valid values:
3119    *    <ul>
3120    *       <li>{@link Content}
3121    *       <li>{@link Header}
3122    *       <li>{@link Query}
3123    *       <li>{@link FormData}
3124    *       <li>{@link Path}
3125    *       <li>{@link Response}
3126    *       <li>{@link HasQuery}
3127    *       <li>{@link HasFormData}
3128    *    </ul>
3129    * @param m
3130    *    The Java method with the return type being checked.
3131    * @return The schema information about the parameter.
3132    */
3133   public static HttpPartSchema create(Class<? extends Annotation> c, Method m) {
3134      return create().applyAll(c, m).build();
3135   }
3136
3137   /**
3138    * Finds the schema information for the specified method parameter.
3139    *
3140    * <p>
3141    * This method will gather all the schema information from the annotations at the following locations:
3142    * <ul>
3143    *    <li>The method parameter.
3144    *    <li>The method parameter class.
3145    *    <li>The method parameter parent classes and interfaces.
3146    * </ul>
3147    *
3148    * @param c
3149    *    The annotation to look for.
3150    *    <br>Valid values:
3151    *    <ul>
3152    *       <li>{@link Content}
3153    *       <li>{@link Header}
3154    *       <li>{@link Query}
3155    *       <li>{@link FormData}
3156    *       <li>{@link Path}
3157    *       <li>{@link Response}
3158    *       <li>{@link HasQuery}
3159    *       <li>{@link HasFormData}
3160    *    </ul>
3161    * @param mpi The Java method parameter.
3162    * @return The schema information about the parameter.
3163    */
3164   public static HttpPartSchema create(Class<? extends Annotation> c, ParameterInfo mpi) {
3165      return create().applyAll(c, mpi).build();
3166   }
3167
3168   /**
3169    * Shortcut for calling <c>create().type(type);</c>
3170    *
3171    * @param type The schema type value.
3172    * @return A new builder.
3173    */
3174   public static Builder create(String type) {
3175      return create().type(type);
3176   }
3177
3178   /**
3179    * Shortcut for calling <c>create().type(type).format(format);</c>
3180    *
3181    * @param type The schema type value.
3182    * @param format The schema format value.
3183    * @return A new builder.
3184    */
3185   public static Builder create(String type, String format) {
3186      return create().type(type).format(format);
3187   }
3188
3189   /**
3190    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>)</c>.
3191    *
3192    * @return A new builder for this object.
3193    */
3194   public static Builder tArray() {
3195      return create().tArray();
3196   }
3197
3198   /**
3199    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).items(items)</c>.
3200    *
3201    * @param items The schema of the array items.
3202    * @return A new builder for this object.
3203    */
3204   public static Builder tArray(Builder items) {
3205      return create().tArray().items(items);
3206   }
3207
3208   /**
3209    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>CSV</jsf>)</c>.
3210    *
3211    * @return A new builder for this object.
3212    */
3213   public static Builder tArrayCsv() {
3214      return create().tArray().cfCsv();
3215   }
3216
3217   /**
3218    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>CSV</jsf>).items(items)</c>.
3219    *
3220    * @param items The schema of the array items.
3221    * @return A new builder for this object.
3222    */
3223   public static Builder tArrayCsv(Builder items) {
3224      return create().tArray().cfCsv().items(items);
3225   }
3226
3227   /**
3228    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>MULTI</jsf>)</c>.
3229    *
3230    * @return A new builder for this object.
3231    */
3232   public static Builder tArrayMulti() {
3233      return create().tArray().cfMulti();
3234   }
3235
3236   /**
3237    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>MULTI</jsf>).items(items)</c>.
3238    *
3239    * @param items The schema of the array items.
3240    * @return A new builder for this object.
3241    */
3242   public static Builder tArrayMulti(Builder items) {
3243      return create().tArray().cfMulti().items(items);
3244   }
3245
3246   /**
3247    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>PIPES</jsf>)</c>.
3248    *
3249    * @return A new builder for this object.
3250    */
3251   public static Builder tArrayPipes() {
3252      return create().tArray().cfPipes();
3253   }
3254
3255   /**
3256    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>PIPES</jsf>).items(items)</c>.
3257    *
3258    * @param items The schema of the array items.
3259    * @return A new builder for this object.
3260    */
3261   public static Builder tArrayPipes(Builder items) {
3262      return create().tArray().cfPipes().items(items);
3263   }
3264
3265   /**
3266    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>SSV</jsf>)</c>.
3267    *
3268    * @return A new builder for this object.
3269    */
3270   public static Builder tArraySsv() {
3271      return create().tArray().cfSsv();
3272   }
3273
3274   /**
3275    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>SSV</jsf>).items(items)</c>.
3276    *
3277    * @param items The schema of the array items.
3278    * @return A new builder for this object.
3279    */
3280   public static Builder tArraySsv(Builder items) {
3281      return create().tArray().cfSsv().items(items);
3282   }
3283
3284   /**
3285    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>TSV</jsf>)</c>.
3286    *
3287    * @return A new builder for this object.
3288    */
3289   public static Builder tArrayTsv() {
3290      return create().tArray().cfTsv();
3291   }
3292
3293   /**
3294    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>TSV</jsf>).items(items)</c>.
3295    *
3296    * @param items The schema of the array items.
3297    * @return A new builder for this object.
3298    */
3299   public static Builder tArrayTsv(Builder items) {
3300      return create().tArray().cfTsv().items(items);
3301   }
3302
3303   /**
3304    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>UONC</jsf>)</c>.
3305    *
3306    * @return A new builder for this object.
3307    */
3308   public static Builder tArrayUon() {
3309      return create().tArray().cfUon();
3310   }
3311
3312   /**
3313    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>ARRAY</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>UONC</jsf>).items(items)</c>.
3314    *
3315    * @param items The schema of the array items.
3316    * @return A new builder for this object.
3317    */
3318   public static Builder tArrayUon(Builder items) {
3319      return create().tArray().cfUon().items(items);
3320   }
3321
3322   /**
3323    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>BINARY</jsf>)</c>.
3324    *
3325    * @return A new builder for this object.
3326    */
3327   public static Builder tBinary() {
3328      return create().tString().fBinary();
3329   }
3330
3331   /**
3332    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>BINARY_SPACED</jsf>)</c>.
3333    *
3334    * @return A new builder for this object.
3335    */
3336   public static Builder tBinarySpaced() {
3337      return create().tString().fBinarySpaced();
3338   }
3339
3340   /**
3341    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>BOOLEAN</jsf>)</c>.
3342    *
3343    * @return A new builder for this object.
3344    */
3345   public static Builder tBoolean() {
3346      return create().tBoolean();
3347   }
3348
3349   /**
3350    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>BYTE</jsf>)</c>.
3351    *
3352    * @return A new builder for this object.
3353    */
3354   public static Builder tByte() {
3355      return create().tString().fByte();
3356   }
3357
3358   /**
3359    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>DATE</jsf>)</c>.
3360    *
3361    * @return A new builder for this object.
3362    */
3363   public static Builder tDate() {
3364      return create().tString().fDate();
3365   }
3366
3367   /**
3368    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>DATE_TIME</jsf>)</c>.
3369    *
3370    * @return A new builder for this object.
3371    */
3372   public static Builder tDateTime() {
3373      return create().tString().fDateTime();
3374   }
3375
3376   /**
3377    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>NUMBER</jsf>).format(HttpPartFormat.<jsf>DOUBLE</jsf>)</c>.
3378    *
3379    * @return A new builder for this object.
3380    */
3381   public static Builder tDouble() {
3382      return create().tNumber().fDouble();
3383   }
3384
3385   /**
3386    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>FILE</jsf>)</c>.
3387    *
3388    * @return A new builder for this object.
3389    */
3390   public static Builder tFile() {
3391      return create().tFile();
3392   }
3393
3394   /**
3395    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>NUMBER</jsf>).format(HttpPartFormat.<jsf>FLOAT</jsf>)</c>.
3396    *
3397    * @return A new builder for this object.
3398    */
3399   public static Builder tFloat() {
3400      return create().tNumber().fFloat();
3401   }
3402
3403   /**
3404    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>INTEGER</jsf>).format(HttpPartFormat.<jsf>INT32</jsf>)</c>.
3405    *
3406    * @return A new builder for this object.
3407    */
3408   public static Builder tInt32() {
3409      return create().tInteger().fInt32();
3410   }
3411
3412   /**
3413    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>INTEGER</jsf>).format(HttpPartFormat.<jsf>INT64</jsf>)</c>.
3414    *
3415    * @return A new builder for this object.
3416    */
3417   public static Builder tInt64() {
3418      return create().tInteger().fInt64();
3419   }
3420
3421   /**
3422    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>INTEGER</jsf>)</c>.
3423    *
3424    * @return A new builder for this object.
3425    */
3426   public static Builder tInteger() {
3427      return create().tInteger();
3428   }
3429
3430   /**
3431    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>NONE</jsf>)</c>.
3432    *
3433    * @return A new builder for this object.
3434    */
3435   public static Builder tNone() {
3436      return create().tNone();
3437   }
3438
3439   /**
3440    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>NUMBER</jsf>)</c>.
3441    *
3442    * @return A new builder for this object.
3443    */
3444   public static Builder tNumber() {
3445      return create().tNumber();
3446   }
3447
3448   /**
3449    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>)</c>.
3450    *
3451    * @return A new builder for this object.
3452    */
3453   public static Builder tObject() {
3454      return create().tObject();
3455   }
3456
3457   /**
3458    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>CSV</jsf>)</c>.
3459    *
3460    * @return A new builder for this object.
3461    */
3462   public static Builder tObjectCsv() {
3463      return create().tObject().cfCsv();
3464   }
3465
3466   /**
3467    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>PIPES</jsf>)</c>.
3468    *
3469    * @return A new builder for this object.
3470    */
3471   public static Builder tObjectPipes() {
3472      return create().tObject().cfPipes();
3473   }
3474
3475   /**
3476    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>SSV</jsf>)</c>.
3477    *
3478    * @return A new builder for this object.
3479    */
3480   public static Builder tObjectSsv() {
3481      return create().tObject().cfSsv();
3482   }
3483
3484   /**
3485    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>TSV</jsf>)</c>.
3486    *
3487    * @return A new builder for this object.
3488    */
3489   public static Builder tObjectTsv() {
3490      return create().tObject().cfTsv();
3491   }
3492
3493   /**
3494    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>OBJECT</jsf>).collectionFormat(HttpPartCollectionFormat.<jsf>UON</jsf>)</c>.
3495    *
3496    * @return A new builder for this object.
3497    */
3498   public static Builder tObjectUon() {
3499      return create().tObject().cfUon();
3500   }
3501
3502   /**
3503    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>)</c>.
3504    *
3505    * @return A new builder for this object.
3506    */
3507   public static Builder tString() {
3508      return create().tString();
3509   }
3510
3511   /**
3512    * Shortcut for <c><jsm>create</jsm>().type(HttpPartDataType.<jsf>STRING</jsf>).format(HttpPartFormat.<jsf>UON</jsf>)</c>.
3513    *
3514    * @return A new builder for this object.
3515    */
3516   public static Builder tUon() {
3517      return create().tString().fUon();
3518   }
3519
3520   private static Map<String,HttpPartSchema> build(Map<String,Object> in, boolean noValidate) {
3521      if (in == null)
3522         return null;
3523      Map<String,HttpPartSchema> m = map();
3524      in.forEach((k, v) -> m.put(k, build(v, noValidate)));
3525      return u(m);
3526   }
3527
3528   private static HttpPartSchema build(Object in, boolean noValidate) {
3529      if (in == null)
3530         return null;
3531      if (in instanceof HttpPartSchema in2)
3532         return in2;
3533      return ((Builder)in).noValidate(noValidate).build();
3534   }
3535
3536   private static <T> Set<T> copy(Set<T> in) {
3537      return in == null ? emptySet() : u(copyOf(in));
3538   }
3539
3540   final static JsonMap toJsonMap(String[] ss) {
3541      String s = StringUtils.joinnl(ss);
3542      if (s.isEmpty())
3543         return null;
3544      if (! isProbablyJsonObject(s, true))
3545         s = "{" + s + "}";
3546      try {
3547         return JsonMap.ofJson(s);
3548      } catch (ParseException e) {
3549         throw toRex(e);
3550      }
3551   }
3552
3553   final static Number toNumber(String...s) {
3554      try {
3555         for (var ss : s)
3556            if (ne(ss))
3557               return parseNumber(ss, Number.class);
3558         return null;
3559      } catch (ParseException e) {
3560         throw toRex(e);
3561      }
3562   }
3563
3564   final static Set<String> toSet(String s) {
3565      if (isEmpty(s))
3566         return null;
3567      Set<String> set = set();
3568      try {
3569         JsonList.ofJsonOrCdl(s).forEach(x -> set.add(x.toString()));
3570      } catch (ParseException e) {
3571         throw toRex(e);
3572      }
3573      return set;
3574   }
3575
3576   final static Set<String> toSet(String[]...s) {
3577      var isNotEmpty = false;
3578      for (var ss : s)
3579         isNotEmpty |= ss.length > 0;
3580      if (! isNotEmpty)
3581         return null;
3582      Set<String> set = set();
3583      for (var ss : s)
3584         if (nn(ss))
3585            for (var ss2 : ss)
3586               StringUtils.split(ss2, x -> set.add(x));
3587      return set.isEmpty() ? null : set;
3588   }
3589
3590   final String name;
3591   final String default_;
3592   final Set<String> enum_;
3593   final Map<String,HttpPartSchema> properties;
3594   final boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty;
3595   final HttpPartCollectionFormat collectionFormat;
3596   final HttpPartDataType type;
3597   final HttpPartFormat format;
3598   final Pattern pattern;
3599   final HttpPartSchema items, additionalProperties;
3600   final Number maximum, minimum, multipleOf;
3601   final Long maxLength, minLength, maxItems, minItems, maxProperties, minProperties;
3602
3603   final Class<? extends HttpPartParser> parser;
3604
3605   final Class<? extends HttpPartSerializer> serializer;
3606
3607   final ClassMeta<?> parsedType;
3608
3609   // JSON Schema Draft 2020-12 fields
3610   final String const_;
3611
3612   final String[] examples;
3613
3614   final boolean deprecated;
3615
3616   final Number exclusiveMaximumValue, exclusiveMinimumValue;
3617
3618   HttpPartSchema(Builder b) {
3619      name = b.name;
3620      default_ = b.default_;
3621      enum_ = copy(b.enum_);
3622      properties = build(b.properties, b.noValidate);
3623      allowEmptyValue = resolve(b.allowEmptyValue);
3624      exclusiveMaximum = resolve(b.exclusiveMaximum);
3625      exclusiveMinimum = resolve(b.exclusiveMinimum);
3626      required = resolve(b.required);
3627      uniqueItems = resolve(b.uniqueItems);
3628      skipIfEmpty = resolve(b.skipIfEmpty);
3629      collectionFormat = b.collectionFormat;
3630      type = b.type;
3631      format = b.format;
3632      pattern = b.pattern;
3633      items = build(b.items, b.noValidate);
3634      additionalProperties = build(b.additionalProperties, b.noValidate);
3635      maximum = b.maximum;
3636      minimum = b.minimum;
3637      multipleOf = b.multipleOf;
3638      maxItems = b.maxItems;
3639      maxLength = b.maxLength;
3640      maxProperties = b.maxProperties;
3641      minItems = b.minItems;
3642      minLength = b.minLength;
3643      minProperties = b.minProperties;
3644      parser = b.parser;
3645      serializer = b.serializer;
3646      // JSON Schema Draft 2020-12 fields
3647      const_ = b.const_;
3648      examples = b.examples;
3649      deprecated = resolve(b.deprecated);
3650      exclusiveMaximumValue = b.exclusiveMaximumValue;
3651      exclusiveMinimumValue = b.exclusiveMinimumValue;
3652
3653      // Calculate parse type
3654      Class<?> parsedType = Object.class;
3655      if (type == ARRAY) {
3656         if (nn(items))
3657            parsedType = Array.newInstance(items.parsedType.inner(), 0).getClass();
3658      } else if (type == BOOLEAN) {
3659         parsedType = Boolean.class;
3660      } else if (type == INTEGER) {
3661         if (format == INT64)
3662            parsedType = Long.class;
3663         else
3664            parsedType = Integer.class;
3665      } else if (type == NUMBER) {
3666         if (format == DOUBLE)
3667            parsedType = Double.class;
3668         else
3669            parsedType = Float.class;
3670      } else if (type == STRING) {
3671         if (format == BYTE || format == BINARY || format == BINARY_SPACED)
3672            parsedType = byte[].class;
3673         else if (format == DATE || format == DATE_TIME)
3674            parsedType = Calendar.class;
3675         else
3676            parsedType = String.class;
3677      }
3678      this.parsedType = BeanContext.DEFAULT.getClassMeta(parsedType);
3679
3680      if (b.noValidate)
3681         return;
3682
3683      // Validation.
3684      var errors = list();
3685      var notAllowed = listb(String.class);
3686      var invalidFormat = false;
3687
3688      // @formatter:off
3689      switch (type) {
3690         case STRING: {
3691            notAllowed
3692               .addIf(nn(properties), "properties")
3693               .addIf(nn(additionalProperties), "additionalProperties")
3694               .addIf(exclusiveMaximum, "exclusiveMaximum")
3695               .addIf(exclusiveMinimum, "exclusiveMinimum")
3696               .addIf(uniqueItems, "uniqueItems")
3697               .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat")
3698               .addIf(nn(items), "items")
3699               .addIf(nn(maximum), "maximum")
3700               .addIf(nn(minimum), "minimum")
3701               .addIf(nn(multipleOf), "multipleOf")
3702               .addIf(nn(maxItems), "maxItems")
3703               .addIf(nn(minItems), "minItems")
3704               .addIf(nn(minProperties), "minProperties");
3705            invalidFormat = ! format.isOneOf(HttpPartFormat.BYTE, HttpPartFormat.BINARY, HttpPartFormat.BINARY_SPACED, HttpPartFormat.DATE, HttpPartFormat.DATE_TIME, HttpPartFormat.PASSWORD, HttpPartFormat.UON, HttpPartFormat.NO_FORMAT);
3706            break;
3707         }
3708         case ARRAY: {
3709            notAllowed.addIf(nn(properties), "properties")
3710               .addIf(nn(additionalProperties), "additionalProperties")
3711               .addIf(exclusiveMaximum, "exclusiveMaximum")
3712               .addIf(exclusiveMinimum, "exclusiveMinimum")
3713               .addIf(nn(pattern), "pattern")
3714               .addIf(nn(maximum), "maximum")
3715               .addIf(nn(minimum), "minimum")
3716               .addIf(nn(multipleOf), "multipleOf")
3717               .addIf(nn(maxLength), "maxLength")
3718               .addIf(nn(minLength), "minLength")
3719               .addIf(nn(maxProperties), "maxProperties")
3720               .addIf(nn(minProperties), "minProperties");
3721            invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON);
3722            break;
3723         }
3724         case BOOLEAN: {
3725            notAllowed.addIf(! enum_.isEmpty(), "enum")
3726               .addIf(nn(properties), "properties")
3727               .addIf(nn(additionalProperties), "additionalProperties")
3728               .addIf(exclusiveMaximum, "exclusiveMaximum")
3729               .addIf(exclusiveMinimum, "exclusiveMinimum")
3730               .addIf(uniqueItems, "uniqueItems")
3731               .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat")
3732               .addIf(nn(pattern), "pattern")
3733               .addIf(nn(items), "items")
3734               .addIf(nn(maximum), "maximum")
3735               .addIf(nn(minimum), "minimum")
3736               .addIf(nn(multipleOf), "multipleOf")
3737               .addIf(nn(maxItems), "maxItems")
3738               .addIf(nn(maxLength), "maxLength")
3739               .addIf(nn(maxProperties), "maxProperties")
3740               .addIf(nn(minItems), "minItems")
3741               .addIf(nn(minLength), "minLength")
3742               .addIf(nn(minProperties), "minProperties");
3743            invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON);
3744            break;
3745         }
3746         case FILE: {
3747            break;
3748         }
3749         case INTEGER: {
3750            notAllowed.addIf(nn(properties), "properties")
3751               .addIf(nn(additionalProperties), "additionalProperties")
3752               .addIf(uniqueItems, "uniqueItems")
3753               .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat")
3754               .addIf(nn(pattern), "pattern")
3755               .addIf(nn(items), "items")
3756               .addIf(nn(maxItems), "maxItems")
3757               .addIf(nn(maxLength), "maxLength")
3758               .addIf(nn(maxProperties), "maxProperties")
3759               .addIf(nn(minItems), "minItems")
3760               .addIf(nn(minLength), "minLength")
3761               .addIf(nn(minProperties), "minProperties");
3762            invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON, HttpPartFormat.INT32, HttpPartFormat.INT64);
3763            break;
3764         }
3765         case NUMBER: {
3766            notAllowed.addIf(nn(properties), "properties")
3767               .addIf(nn(additionalProperties), "additionalProperties")
3768               .addIf(uniqueItems, "uniqueItems")
3769               .addIf(collectionFormat != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat")
3770               .addIf(nn(pattern), "pattern")
3771               .addIf(nn(items), "items")
3772               .addIf(nn(maxItems), "maxItems")
3773               .addIf(nn(maxLength), "maxLength")
3774               .addIf(nn(maxProperties), "maxProperties")
3775               .addIf(nn(minItems), "minItems")
3776               .addIf(nn(minLength), "minLength")
3777               .addIf(nn(minProperties), "minProperties");
3778            invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT, HttpPartFormat.UON, HttpPartFormat.FLOAT, HttpPartFormat.DOUBLE);
3779            break;
3780         }
3781         case OBJECT: {
3782            notAllowed.addIf(exclusiveMaximum, "exclusiveMaximum")
3783               .addIf(exclusiveMinimum, "exclusiveMinimum")
3784               .addIf(uniqueItems, "uniqueItems")
3785               .addIf(nn(pattern), "pattern")
3786               .addIf(nn(items), "items")
3787               .addIf(nn(maximum), "maximum")
3788               .addIf(nn(minimum), "minimum")
3789               .addIf(nn(multipleOf), "multipleOf")
3790               .addIf(nn(maxItems), "maxItems")
3791               .addIf(nn(maxLength), "maxLength")
3792               .addIf(nn(minItems), "minItems")
3793               .addIf(nn(minLength), "minLength");
3794            invalidFormat = ! format.isOneOf(HttpPartFormat.NO_FORMAT);
3795            break;
3796         }
3797         default:
3798            break;
3799      }
3800      // @formatter:on
3801
3802      List<String> notAllowed2 = notAllowed.build();
3803      if (! notAllowed2.isEmpty())
3804         errors.add("Attributes not allow for type='" + type + "': " + StringUtils.join(notAllowed2, ","));
3805      if (invalidFormat)
3806         errors.add("Invalid format for type='" + type + "': '" + format + "'");
3807      if (exclusiveMaximum && maximum == null)
3808         errors.add("Cannot specify exclusiveMaximum with maximum.");
3809      if (exclusiveMinimum && minimum == null)
3810         errors.add("Cannot specify exclusiveMinimum with minimum.");
3811      if (required && nn(default_))
3812         errors.add("Cannot specify a default value on a required value.");
3813      if (nn(minLength) && nn(maxLength) && maxLength < minLength)
3814         errors.add("maxLength cannot be less than minLength.");
3815      if (nn(minimum) && nn(maximum) && maximum.doubleValue() < minimum.doubleValue())
3816         errors.add("maximum cannot be less than minimum.");
3817      if (nn(minItems) && nn(maxItems) && maxItems < minItems)
3818         errors.add("maxItems cannot be less than minItems.");
3819      if (nn(minProperties) && nn(maxProperties) && maxProperties < minProperties)
3820         errors.add("maxProperties cannot be less than minProperties.");
3821      if (nn(minLength) && minLength < 0)
3822         errors.add("minLength cannot be less than zero.");
3823      if (nn(maxLength) && maxLength < 0)
3824         errors.add("maxLength cannot be less than zero.");
3825      if (nn(minItems) && minItems < 0)
3826         errors.add("minItems cannot be less than zero.");
3827      if (nn(maxItems) && maxItems < 0)
3828         errors.add("maxItems cannot be less than zero.");
3829      if (nn(minProperties) && minProperties < 0)
3830         errors.add("minProperties cannot be less than zero.");
3831      if (nn(maxProperties) && maxProperties < 0)
3832         errors.add("maxProperties cannot be less than zero.");
3833      if (type == ARRAY && nn(items) && items.getType() == OBJECT && (format != UON && format != HttpPartFormat.NO_FORMAT))
3834         errors.add("Cannot define an array of objects unless array format is 'uon'.");
3835
3836      if (! errors.isEmpty())
3837         throw new ContextRuntimeException("Schema specification errors: \n\t" + StringUtils.join(errors, "\n\t"), new Object[0]);
3838   }
3839
3840   /**
3841    * Returns the <c>collectionFormat</c> field of this schema.
3842    *
3843    * @return The <c>collectionFormat</c> field of this schema, or <jk>null</jk> if not specified.
3844    * @see HttpPartSchema.Builder#collectionFormat(String)
3845    */
3846   public HttpPartCollectionFormat getCollectionFormat() { return collectionFormat; }
3847
3848   /**
3849    * Returns the <c>default</c> field of this schema.
3850    *
3851    * @return The default value for this schema, or <jk>null</jk> if not specified.
3852    * @see HttpPartSchema.Builder#default_(String)
3853    */
3854   public String getDefault() { return default_; }
3855
3856   /**
3857    * Returns the <c>enum</c> field of this schema.
3858    *
3859    * @return The <c>enum</c> field of this schema, or <jk>null</jk> if not specified.
3860    * @see HttpPartSchema.Builder#enum_(Set)
3861    */
3862   public Set<String> getEnum() { return enum_; }
3863
3864   /**
3865    * Returns the <c>format</c> field of this schema.
3866    *
3867    * @see HttpPartSchema.Builder#format(String)
3868    * @return The <c>format</c> field of this schema, or <jk>null</jk> if not specified.
3869    */
3870   public HttpPartFormat getFormat() { return format; }
3871
3872   /**
3873    * Returns the <c>format</c> field of this schema.
3874    *
3875    * @param cm
3876    *    The class meta of the object.
3877    *    <br>Used to auto-detect the format if the format was not specified.
3878    * @return The <c>format</c> field of this schema, or <jk>null</jk> if not specified.
3879    * @see HttpPartSchema.Builder#format(String)
3880    */
3881   public HttpPartFormat getFormat(ClassMeta<?> cm) {
3882      if (format != HttpPartFormat.NO_FORMAT)
3883         return format;
3884      if (cm.isNumber()) {
3885         if (cm.isDecimal()) {
3886            if (cm.isDouble())
3887               return HttpPartFormat.DOUBLE;
3888            return HttpPartFormat.FLOAT;
3889         }
3890         if (cm.isLong())
3891            return HttpPartFormat.INT64;
3892         return HttpPartFormat.INT32;
3893      }
3894      return format;
3895   }
3896
3897   /**
3898    * Returns the <c>maximum</c> field of this schema.
3899    *
3900    * @return The schema for child items of the object represented by this schema, or <jk>null</jk> if not defined.
3901    * @see HttpPartSchema.Builder#items(HttpPartSchema.Builder)
3902    */
3903   public HttpPartSchema getItems() { return items; }
3904
3905   /**
3906    * Returns the <c>maximum</c> field of this schema.
3907    *
3908    * @return The <c>maximum</c> field of this schema, or <jk>null</jk> if not specified.
3909    * @see HttpPartSchema.Builder#maximum(Number)
3910    */
3911   public Number getMaximum() { return maximum; }
3912
3913   /**
3914    * Returns the <c>xxx</c> field of this schema.
3915    *
3916    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3917    * @see HttpPartSchema.Builder#maxItems(Long)
3918    */
3919   public Long getMaxItems() { return maxItems; }
3920
3921   /**
3922    * Returns the <c>xxx</c> field of this schema.
3923    *
3924    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3925    * @see HttpPartSchema.Builder#maxLength(Long)
3926    */
3927   public Long getMaxLength() { return maxLength; }
3928
3929   /**
3930    * Returns the <c>xxx</c> field of this schema.
3931    *
3932    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3933    * @see HttpPartSchema.Builder#maxProperties(Long)
3934    */
3935   public Long getMaxProperties() { return maxProperties; }
3936
3937   /**
3938    * Returns the <c>minimum</c> field of this schema.
3939    *
3940    * @return The <c>minimum</c> field of this schema, or <jk>null</jk> if not specified.
3941    * @see HttpPartSchema.Builder#minimum(Number)
3942    */
3943   public Number getMinimum() { return minimum; }
3944
3945   /**
3946    * Returns the <c>xxx</c> field of this schema.
3947    *
3948    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3949    * @see HttpPartSchema.Builder#minItems(Long)
3950    */
3951   public Long getMinItems() { return minItems; }
3952
3953   /**
3954    * Returns the <c>xxx</c> field of this schema.
3955    *
3956    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3957    * @see HttpPartSchema.Builder#minLength(Long)
3958    */
3959   public Long getMinLength() { return minLength; }
3960
3961   /**
3962    * Returns the <c>xxx</c> field of this schema.
3963    *
3964    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3965    * @see HttpPartSchema.Builder#minProperties(Long)
3966    */
3967   public Long getMinProperties() { return minProperties; }
3968
3969   /**
3970    * Returns the <c>xxx</c> field of this schema.
3971    *
3972    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
3973    * @see HttpPartSchema.Builder#multipleOf(Number)
3974    */
3975   public Number getMultipleOf() { return multipleOf; }
3976
3977   /**
3978    * Returns the name of the object described by this schema, for example the query or form parameter name.
3979    *
3980    * @return The name, or <jk>null</jk> if not specified.
3981    * @see HttpPartSchema.Builder#name(String)
3982    */
3983   public String getName() { return name; }
3984
3985   /**
3986    * Returns the default parsed type for this schema.
3987    *
3988    * @return The default parsed type for this schema.  Never <jk>null</jk>.
3989    */
3990   public ClassMeta<?> getParsedType() { return parsedType; }
3991
3992   /**
3993    * Returns the <c>parser</c> field of this schema.
3994    *
3995    * @return The <c>parser</c> field of this schema, or <jk>null</jk> if not specified.
3996    * @see HttpPartSchema.Builder#parser(Class)
3997    */
3998   public Class<? extends HttpPartParser> getParser() { return parser; }
3999
4000   /**
4001    * Returns the <c>xxx</c> field of this schema.
4002    *
4003    * @return The <c>xxx</c> field of this schema, or <jk>null</jk> if not specified.
4004    * @see HttpPartSchema.Builder#pattern(String)
4005    */
4006   public Pattern getPattern() { return pattern; }
4007
4008   /**
4009    * Returns the schema information for the specified property.
4010    *
4011    * @param name The property name.
4012    * @return The schema information for the specified property, or <jk>null</jk> if properties are not defined on this schema.
4013    */
4014   public HttpPartSchema getProperty(String name) {
4015      if (nn(properties)) {
4016         HttpPartSchema schema = properties.get(name);
4017         if (nn(schema))
4018            return schema;
4019      }
4020      return additionalProperties;
4021   }
4022
4023   /**
4024    * Returns the <c>serializer</c> field of this schema.
4025    *
4026    * @return The <c>serializer</c> field of this schema, or <jk>null</jk> if not specified.
4027    * @see HttpPartSchema.Builder#serializer(Class)
4028    */
4029   public Class<? extends HttpPartSerializer> getSerializer() { return serializer; }
4030
4031   /**
4032    * Returns the <c>type</c> field of this schema.
4033    *
4034    * @return The <c>type</c> field of this schema, or <jk>null</jk> if not specified.
4035    * @see HttpPartSchema.Builder#type(String)
4036    */
4037   public HttpPartDataType getType() { return type; }
4038
4039   /**
4040    * Returns the <c>type</c> field of this schema.
4041    *
4042    * @param cm
4043    *    The class meta of the object.
4044    *    <br>Used to auto-detect the type if the type was not specified.
4045    * @return The format field of this schema, or <jk>null</jk> if not specified.
4046    * @see HttpPartSchema.Builder#format(String)
4047    */
4048   public HttpPartDataType getType(ClassMeta<?> cm) {
4049      if (type != HttpPartDataType.NO_TYPE)
4050         return type;
4051      if (cm.isTemporal() || cm.isDateOrCalendar())
4052         return HttpPartDataType.STRING;
4053      if (cm.isNumber()) {
4054         if (cm.isDecimal())
4055            return HttpPartDataType.NUMBER;
4056         return HttpPartDataType.INTEGER;
4057      }
4058      if (cm.isBoolean())
4059         return HttpPartDataType.BOOLEAN;
4060      if (cm.isMapOrBean())
4061         return HttpPartDataType.OBJECT;
4062      if (cm.isCollectionOrArray())
4063         return HttpPartDataType.ARRAY;
4064      return HttpPartDataType.STRING;
4065   }
4066
4067   /**
4068    * Returns <jk>true</jk> if this schema has properties associated with it.
4069    *
4070    * @return <jk>true</jk> if this schema has properties associated with it.
4071    */
4072   public boolean hasProperties() {
4073      return nn(properties) || nn(additionalProperties);
4074   }
4075
4076   /**
4077    * Returns the <c>allowEmptyValue</c> field of this schema.
4078    *
4079    * @return The <c>skipIfEmpty</c> field of this schema.
4080    * @see HttpPartSchema.Builder#skipIfEmpty(Boolean)
4081    */
4082   public boolean isAllowEmptyValue() { return allowEmptyValue; }
4083
4084   /**
4085    * Returns the <c>exclusiveMaximum</c> field of this schema.
4086    *
4087    * @return The <c>exclusiveMaximum</c> field of this schema.
4088    * @see HttpPartSchema.Builder#exclusiveMaximum(Boolean)
4089    */
4090   public boolean isExclusiveMaximum() { return exclusiveMaximum; }
4091
4092   /**
4093    * Returns the <c>exclusiveMinimum</c> field of this schema.
4094    *
4095    * @return The <c>exclusiveMinimum</c> field of this schema.
4096    * @see HttpPartSchema.Builder#exclusiveMinimum(Boolean)
4097    */
4098   public boolean isExclusiveMinimum() { return exclusiveMinimum; }
4099
4100   /**
4101    * Returns the <c>required</c> field of this schema.
4102    *
4103    * @return The <c>required</c> field of this schema.
4104    * @see HttpPartSchema.Builder#required(Boolean)
4105    */
4106   public boolean isRequired() { return required; }
4107
4108   /**
4109    * Returns the <c>skipIfEmpty</c> field of this schema.
4110    *
4111    * @return The <c>skipIfEmpty</c> field of this schema.
4112    * @see HttpPartSchema.Builder#skipIfEmpty(Boolean)
4113    */
4114   public boolean isSkipIfEmpty() { return skipIfEmpty; }
4115
4116   /**
4117    * Returns the <c>uniqueItems</c> field of this schema.
4118    *
4119    * @return The <c>uniqueItems</c> field of this schema.
4120    * @see HttpPartSchema.Builder#uniqueItems(Boolean)
4121    */
4122   public boolean isUniqueItems() { return uniqueItems; }
4123
4124   protected FluentMap<String,Object> properties() {
4125      // @formatter:off
4126      Predicate<Object> ne = x -> ne(s(x));
4127      Predicate<Object> nf = x -> x instanceof Boolean && (Boolean)x;
4128      Predicate<Object> nm1 = x -> x instanceof Number && ((Number)x).intValue() != -1;
4129      Predicate<Object> nn = Utils::nn;
4130      return mapb_so().sorted().buildFluent()
4131         .ai(ne, "name", name)
4132         .ai(ne, "type", type)
4133         .ai(ne, "format", format)
4134         .ai(ne, "default", default_)
4135         .ai(ne, "enum", enum_)
4136         .ai(ne, "properties", properties)
4137         .ai(nf, "allowEmptyValue", allowEmptyValue)
4138         .ai(nf, "exclusiveMaximum", exclusiveMaximum)
4139         .ai(nf, "exclusiveMinimum", exclusiveMinimum)
4140         .ai(nf, "required", required)
4141         .ai(nf, "uniqueItems", uniqueItems)
4142         .ai(nf, "skipIfEmpty", skipIfEmpty)
4143         .ai(x -> x != HttpPartCollectionFormat.NO_COLLECTION_FORMAT, "collectionFormat", collectionFormat)
4144         .ai(ne, "pattern", pattern)
4145         .ai(nn, "items", items)
4146         .ai(nn, "additionalProperties", additionalProperties)
4147         .ai(nm1, "maximum", maximum)
4148         .ai(nm1, "minimum", minimum)
4149         .ai(nm1, "multipleOf", multipleOf)
4150         .ai(nm1, "maxLength", maxLength)
4151         .ai(nm1, "minLength", minLength)
4152         .ai(nm1, "maxItems", maxItems)
4153         .ai(nm1, "minItems", minItems)
4154         .ai(nm1, "maxProperties", maxProperties)
4155         .ai(nm1, "minProperties", minProperties)
4156         .a("parsedType", parsedType);
4157      // @formatter:on
4158   }
4159
4160   @Override /* Overridden from Object */
4161   public String toString() {
4162      return r(properties());
4163   }
4164
4165   /**
4166    * Throws a {@link ParseException} if the specified pre-parsed input does not validate against this schema.
4167    *
4168    * @param in The input.
4169    * @return The same object passed in.
4170    * @throws SchemaValidationException if the specified pre-parsed input does not validate against this schema.
4171    */
4172   public String validateInput(String in) throws SchemaValidationException {
4173      if (! isValidRequired(in))
4174         throw new SchemaValidationException("No value specified.");
4175      if (nn(in)) {
4176         if (! isValidAllowEmpty(in))
4177            throw new SchemaValidationException("Empty value not allowed.");
4178         if (! isValidConst(in))
4179            throw new SchemaValidationException("Value does not match constant.  Must be: {0}", const_);
4180         if (! isValidPattern(in))
4181            throw new SchemaValidationException("Value does not match expected pattern.  Must match pattern: {0}", pattern.pattern());
4182         if (! isValidEnum(in))
4183            throw new SchemaValidationException("Value does not match one of the expected values.  Must be one of the following:  {0}", toCdl(enum_));
4184         if (! isValidMaxLength(in))
4185            throw new SchemaValidationException("Maximum length of value exceeded.");
4186         if (! isValidMinLength(in))
4187            throw new SchemaValidationException("Minimum length of value not met.");
4188         if (! isValidFormat(in))
4189            throw new SchemaValidationException("Value does not match expected format: {0}", format);
4190      }
4191      return in;
4192   }
4193
4194   /**
4195    * Throws a {@link ParseException} if the specified parsed output does not validate against this schema.
4196    *
4197    * @param <T> The return type.
4198    * @param o The parsed output.
4199    * @param bc The bean context used to detect POJO types.
4200    * @return The same object passed in.
4201    * @throws SchemaValidationException if the specified parsed output does not validate against this schema.
4202    */
4203   public <T> T validateOutput(T o, BeanContext bc) throws SchemaValidationException {
4204      if (o == null) {
4205         if (! isValidRequired(o))
4206            throw new SchemaValidationException("Required value not provided.");
4207         return o;
4208      }
4209      var cm = bc.getClassMetaForObject(o);
4210      switch (getType(cm)) {
4211         case ARRAY: {
4212            if (cm.isArray()) {
4213               if (! isValidMinItems(o))
4214                  throw new SchemaValidationException("Minimum number of items not met.");
4215               if (! isValidMaxItems(o))
4216                  throw new SchemaValidationException("Maximum number of items exceeded.");
4217               if (! isValidUniqueItems(o))
4218                  throw new SchemaValidationException("Duplicate items not allowed.");
4219               HttpPartSchema items = getItems();
4220               if (nn(items))
4221                  for (var i = 0; i < Array.getLength(o); i++)
4222                     items.validateOutput(Array.get(o, i), bc);
4223            } else if (cm.isCollection()) {
4224               Collection<?> c = (Collection<?>)o;
4225               if (! isValidMinItems(c))
4226                  throw new SchemaValidationException("Minimum number of items not met.");
4227               if (! isValidMaxItems(c))
4228                  throw new SchemaValidationException("Maximum number of items exceeded.");
4229               if (! isValidUniqueItems(c))
4230                  throw new SchemaValidationException("Duplicate items not allowed.");
4231               HttpPartSchema items = getItems();
4232               if (nn(items))
4233                  c.forEach(x -> items.validateOutput(x, bc));
4234            }
4235            break;
4236         }
4237         case INTEGER: {
4238            if (cm.isNumber()) {
4239               Number n = (Number)o;
4240               if (! isValidMinimum(n))
4241                  throw new SchemaValidationException("Minimum value not met.");
4242               if (! isValidMaximum(n))
4243                  throw new SchemaValidationException("Maximum value exceeded.");
4244               if (! isValidMultipleOf(n))
4245                  throw new SchemaValidationException("Multiple-of not met.");
4246            }
4247            break;
4248         }
4249         case NUMBER: {
4250            if (cm.isNumber()) {
4251               Number n = (Number)o;
4252               if (! isValidMinimum(n))
4253                  throw new SchemaValidationException("Minimum value not met.");
4254               if (! isValidMaximum(n))
4255                  throw new SchemaValidationException("Maximum value exceeded.");
4256               if (! isValidMultipleOf(n))
4257                  throw new SchemaValidationException("Multiple-of not met.");
4258            }
4259            break;
4260         }
4261         case OBJECT: {
4262            if (cm.isMapOrBean()) {
4263               Map<?,?> m = cm.isMap() ? (Map<?,?>)o : bc.toBeanMap(o);
4264               if (! isValidMinProperties(m))
4265                  throw new SchemaValidationException("Minimum number of properties not met.");
4266               if (! isValidMaxProperties(m))
4267                  throw new SchemaValidationException("Maximum number of properties exceeded.");
4268               m.forEach((k, v) -> {
4269                  var key = k.toString();
4270                  HttpPartSchema s2 = getProperty(key);
4271                  if (nn(s2))
4272                     s2.validateOutput(v, bc);
4273               });
4274            } else if (cm.isBean()) {
4275
4276            }
4277            break;
4278         }
4279         case STRING: {
4280            if (cm.isCharSequence()) {
4281               var s = o.toString();
4282               if (! isValidMinLength(s))
4283                  throw new SchemaValidationException("Minimum length of value not met.");
4284               if (! isValidMaxLength(s))
4285                  throw new SchemaValidationException("Maximum length of value exceeded.");
4286               if (! isValidPattern(s))
4287                  throw new SchemaValidationException("Value does not match expected pattern.  Must match pattern: {0}", pattern.pattern());
4288               if (! isValidFormat(s))
4289                  throw new SchemaValidationException("Value does not match expected format: {0}", format);
4290            }
4291            break;
4292         }
4293         case BOOLEAN:
4294         case FILE:
4295         case NO_TYPE:
4296            break;
4297         default:
4298            break;
4299      }
4300      return o;
4301   }
4302
4303   private boolean isValidAllowEmpty(String x) {
4304      return allowEmptyValue || ne(x);
4305   }
4306
4307   private boolean isValidConst(String x) {
4308      return const_ == null || const_.equals(x);
4309   }
4310
4311   private static boolean isValidDate(String x) {
4312      // RFC 3339 full-date: YYYY-MM-DD (relaxed to allow various date formats)
4313      return x.matches("^\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}.*");
4314   }
4315
4316   private static boolean isValidDateTime(String x) {
4317      // RFC 3339 date-time (relaxed to allow various datetime formats)
4318      return x.matches("^\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}[T\\s]\\d{1,2}:\\d{1,2}.*");
4319   }
4320
4321   private static boolean isValidDateTimeZone(String x) {
4322      // RFC 3339 date-time with time zone
4323      return x.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?[+-]\\d{2}:\\d{2}$");
4324   }
4325
4326   private static boolean isValidDuration(String x) {
4327      // RFC 3339 Appendix A duration (ISO 8601)
4328      return x.matches("^P(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?$");
4329   }
4330
4331   private static boolean isValidEmail(String x) {
4332      // RFC 5321 simplified email validation
4333      return x.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
4334   }
4335
4336   private boolean isValidEnum(String x) {
4337      return enum_.isEmpty() || enum_.contains(x);
4338   }
4339
4340   private boolean isValidFormat(String x) {
4341      if (format == null || format == HttpPartFormat.NO_FORMAT)
4342         return true;
4343
4344      // Skip validation for literal "null" string
4345      if ("null".equals(x))
4346         return true;
4347
4348      try {
4349         return switch (format) {
4350            case EMAIL -> isValidEmail(x);
4351            case IDN_EMAIL -> isValidIdnEmail(x);
4352            case HOSTNAME -> isValidHostname(x);
4353            case IDN_HOSTNAME -> isValidIdnHostname(x);
4354            case IPV4 -> isValidIpv4(x);
4355            case IPV6 -> isValidIpv6(x);
4356            case URI -> isValidUri(x);
4357            case URI_REFERENCE -> isValidUriReference(x);
4358            case IRI -> isValidIri(x);
4359            case IRI_REFERENCE -> isValidIriReference(x);
4360            case UUID -> isValidUuid(x);
4361            case URI_TEMPLATE -> isValidUriTemplate(x);
4362            case JSON_POINTER -> isValidJsonPointer(x);
4363            case RELATIVE_JSON_POINTER -> isValidRelativeJsonPointer(x);
4364            case REGEX -> isValidRegex(x);
4365            case DATE -> isValidDate(x);
4366            case DATE_TIME -> isValidDateTime(x);
4367            case DATE_TIME_ZONE -> isValidDateTimeZone(x);
4368            case TIME -> isValidTime(x);
4369            case DURATION -> isValidDuration(x);
4370            case BYTE, BINARY, BINARY_SPACED -> true; // These are transformation formats, not validation formats
4371            case PASSWORD -> true; // Password format is just a UI hint
4372            case INT32, INT64, FLOAT, DOUBLE -> true; // Numeric formats are validated during parsing
4373            case UON -> true; // UON format is validated during parsing
4374            default -> true;
4375         };
4376      } catch (@SuppressWarnings("unused") Exception e) {
4377         return false;
4378      }
4379   }
4380
4381   private static boolean isValidHostname(String x) {
4382      // RFC 1123 hostname validation
4383      return x.matches("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)*[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?$");
4384   }
4385
4386   private static boolean isValidIdnEmail(String x) {
4387      // RFC 6531 - allows international characters
4388      return x.matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$");
4389   }
4390
4391   private static boolean isValidIdnHostname(String x) {
4392      // RFC 5890 - allows international characters
4393      return x.matches("^[^\\s]+$");
4394   }
4395
4396   private static boolean isValidIpv4(String x) {
4397      // RFC 2673 IPv4 validation
4398      var parts = x.split("\\.");
4399      if (parts.length != 4)
4400         return false;
4401      for (var part : parts) {
4402         try {
4403            int val = Integer.parseInt(part);
4404            if (val < 0 || val > 255)
4405               return false;
4406         } catch (@SuppressWarnings("unused") NumberFormatException e) {
4407            return false;
4408         }
4409      }
4410      return true;
4411   }
4412
4413   private static boolean isValidIpv6(String x) {
4414      // RFC 4291 IPv6 validation (simplified)
4415      return x.matches("^([0-9a-fA-F]{0,4}:){7}[0-9a-fA-F]{0,4}$|^::([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$|^([0-9a-fA-F]{0,4}:){1,7}:$");
4416   }
4417
4418   private static boolean isValidIri(String x) {
4419      // RFC 3987 IRI validation (allows international characters)
4420      return x.matches("^[a-zA-Z][a-zA-Z0-9+.-]*:.+");
4421   }
4422
4423   private static boolean isValidIriReference(String x) {
4424      // RFC 3987 IRI reference (allows international characters)
4425      return x.length() > 0;
4426   }
4427
4428   private static boolean isValidJsonPointer(String x) {
4429      // RFC 6901 JSON Pointer validation
4430      return x.isEmpty() || x.matches("^(/[^/]*)*$");
4431   }
4432
4433   private boolean isValidMaximum(Number x) {
4434      // Check Draft 2020-12 exclusiveMaximumValue first (takes precedence)
4435      if (nn(exclusiveMaximumValue)) {
4436         if (x instanceof Integer || x instanceof AtomicInteger)
4437            return x.intValue() < exclusiveMaximumValue.intValue();
4438         if (x instanceof Short || x instanceof Byte)
4439            return x.shortValue() < exclusiveMaximumValue.shortValue();
4440         if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger)
4441            return x.longValue() < exclusiveMaximumValue.longValue();
4442         if (x instanceof Float)
4443            return x.floatValue() < exclusiveMaximumValue.floatValue();
4444         if (x instanceof Double || x instanceof BigDecimal)
4445            return x.doubleValue() < exclusiveMaximumValue.doubleValue();
4446      }
4447      // Fall back to Draft 04 boolean exclusiveMaximum with maximum
4448      if (x instanceof Integer || x instanceof AtomicInteger)
4449         return maximum == null || x.intValue() < maximum.intValue() || (x.intValue() == maximum.intValue() && (! exclusiveMaximum));
4450      if (x instanceof Short || x instanceof Byte)
4451         return maximum == null || x.shortValue() < maximum.shortValue() || (x.intValue() == maximum.shortValue() && (! exclusiveMaximum));
4452      if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger)
4453         return maximum == null || x.longValue() < maximum.longValue() || (x.intValue() == maximum.longValue() && (! exclusiveMaximum));
4454      if (x instanceof Float)
4455         return maximum == null || x.floatValue() < maximum.floatValue() || (x.floatValue() == maximum.floatValue() && (! exclusiveMaximum));
4456      if (x instanceof Double || x instanceof BigDecimal)
4457         return maximum == null || x.doubleValue() < maximum.doubleValue() || (x.doubleValue() == maximum.doubleValue() && (! exclusiveMaximum));
4458      return true;
4459   }
4460
4461   private boolean isValidMaxItems(Collection<?> x) {
4462      return maxItems == null || x.size() <= maxItems;
4463   }
4464
4465   private boolean isValidMaxItems(Object x) {
4466      return maxItems == null || Array.getLength(x) <= maxItems;
4467   }
4468
4469   private boolean isValidMaxLength(String x) {
4470      return maxLength == null || x.length() <= maxLength;
4471   }
4472
4473   private boolean isValidMaxProperties(Map<?,?> x) {
4474      return maxProperties == null || x.size() <= maxProperties;
4475   }
4476
4477   private boolean isValidMinimum(Number x) {
4478      // Check Draft 2020-12 exclusiveMinimumValue first (takes precedence)
4479      if (nn(exclusiveMinimumValue)) {
4480         if (x instanceof Integer || x instanceof AtomicInteger)
4481            return x.intValue() > exclusiveMinimumValue.intValue();
4482         if (x instanceof Short || x instanceof Byte)
4483            return x.shortValue() > exclusiveMinimumValue.shortValue();
4484         if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger)
4485            return x.longValue() > exclusiveMinimumValue.longValue();
4486         if (x instanceof Float)
4487            return x.floatValue() > exclusiveMinimumValue.floatValue();
4488         if (x instanceof Double || x instanceof BigDecimal)
4489            return x.doubleValue() > exclusiveMinimumValue.doubleValue();
4490      }
4491      // Fall back to Draft 04 boolean exclusiveMinimum with minimum
4492      if (x instanceof Integer || x instanceof AtomicInteger)
4493         return minimum == null || x.intValue() > minimum.intValue() || (x.intValue() == minimum.intValue() && (! exclusiveMinimum));
4494      if (x instanceof Short || x instanceof Byte)
4495         return minimum == null || x.shortValue() > minimum.shortValue() || (x.intValue() == minimum.shortValue() && (! exclusiveMinimum));
4496      if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger)
4497         return minimum == null || x.longValue() > minimum.longValue() || (x.intValue() == minimum.longValue() && (! exclusiveMinimum));
4498      if (x instanceof Float)
4499         return minimum == null || x.floatValue() > minimum.floatValue() || (x.floatValue() == minimum.floatValue() && (! exclusiveMinimum));
4500      if (x instanceof Double || x instanceof BigDecimal)
4501         return minimum == null || x.doubleValue() > minimum.doubleValue() || (x.doubleValue() == minimum.doubleValue() && (! exclusiveMinimum));
4502      return true;
4503   }
4504
4505   private boolean isValidMinItems(Collection<?> x) {
4506      return minItems == null || x.size() >= minItems;
4507   }
4508
4509   private boolean isValidMinItems(Object x) {
4510      return minItems == null || Array.getLength(x) >= minItems;
4511   }
4512
4513   private boolean isValidMinLength(String x) {
4514      return minLength == null || x.length() >= minLength;
4515   }
4516
4517   private boolean isValidMinProperties(Map<?,?> x) {
4518      return minProperties == null || x.size() >= minProperties;
4519   }
4520
4521   private boolean isValidMultipleOf(Number x) {
4522      if (x instanceof Integer || x instanceof AtomicInteger)
4523         return multipleOf == null || x.intValue() % multipleOf.intValue() == 0;
4524      if (x instanceof Short || x instanceof Byte)
4525         return multipleOf == null || x.shortValue() % multipleOf.shortValue() == 0;
4526      if (x instanceof Long || x instanceof AtomicLong || x instanceof BigInteger)
4527         return multipleOf == null || x.longValue() % multipleOf.longValue() == 0;
4528      if (x instanceof Float)
4529         return multipleOf == null || x.floatValue() % multipleOf.floatValue() == 0;
4530      if (x instanceof Double || x instanceof BigDecimal)
4531         return multipleOf == null || x.doubleValue() % multipleOf.doubleValue() == 0;
4532      return true;
4533   }
4534
4535   private boolean isValidPattern(String x) {
4536      return pattern == null || pattern.matcher(x).matches();
4537   }
4538
4539   private static boolean isValidRegex(String x) {
4540      // ECMA-262 regex validation
4541      try {
4542         java.util.regex.Pattern.compile(x);
4543         return true;
4544      } catch (@SuppressWarnings("unused") Exception e) {
4545         return false;
4546      }
4547   }
4548
4549   private static boolean isValidRelativeJsonPointer(String x) {
4550      // Relative JSON Pointer validation
4551      return x.matches("^(0|[1-9][0-9]*)(#|(/[^/]*)*)$");
4552   }
4553
4554   private boolean isValidRequired(Object x) {
4555      return nn(x) || ! required;
4556   }
4557
4558   private static boolean isValidTime(String x) {
4559      // RFC 3339 time
4560      return x.matches("^\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})?$");
4561   }
4562
4563   private boolean isValidUniqueItems(Collection<?> x) {
4564      if (uniqueItems && ! (x instanceof Set)) {
4565         var s = new HashSet<>();
4566         for (var o : x)
4567            if (! s.add(o))
4568               return false;
4569      }
4570      return true;
4571   }
4572
4573   private boolean isValidUniqueItems(Object x) {
4574      if (uniqueItems) {
4575         var s = new HashSet<>();
4576         for (var i = 0; i < Array.getLength(x); i++) {
4577            var o = Array.get(x, i);
4578            if (! s.add(o))
4579               return false;
4580         }
4581      }
4582      return true;
4583   }
4584
4585   @SuppressWarnings("unused")
4586   private static boolean isValidUri(String x) {
4587      // RFC 3986 URI validation
4588      try {
4589         new java.net.URI(x);
4590         return x.matches("^[a-zA-Z][a-zA-Z0-9+.-]*:.*");
4591      } catch (Exception e) {
4592         return false;
4593      }
4594   }
4595
4596   @SuppressWarnings("unused")
4597   private static boolean isValidUriReference(String x) {
4598      // RFC 3986 URI reference (can be relative)
4599      try {
4600         new java.net.URI(x);
4601         return true;
4602      } catch (Exception e) {
4603         return false;
4604      }
4605   }
4606
4607   private static boolean isValidUriTemplate(String x) {
4608      // RFC 6570 URI Template validation (simplified)
4609      return x.matches("^[^\\s]*$");
4610   }
4611
4612   private static boolean isValidUuid(String x) {
4613      // RFC 4122 UUID validation
4614      return x.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
4615   }
4616
4617   private static boolean resolve(Boolean b) {
4618      return b == null ? false : b;
4619   }
4620}