View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.httppart;
18  
19  import static java.util.Collections.*;
20  import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
21  import static org.apache.juneau.commons.utils.ClassUtils.*;
22  import static org.apache.juneau.commons.utils.CollectionUtils.*;
23  import static org.apache.juneau.commons.utils.StringUtils.*;
24  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
25  import static org.apache.juneau.commons.utils.Utils.*;
26  import static org.apache.juneau.Constants.*;
27  import static org.apache.juneau.httppart.HttpPartDataType.*;
28  import static org.apache.juneau.httppart.HttpPartFormat.*;
29  
30  import java.lang.annotation.*;
31  import java.lang.reflect.*;
32  import java.math.*;
33  import java.util.*;
34  import java.util.concurrent.atomic.*;
35  import java.util.function.*;
36  import java.util.regex.*;
37  
38  import org.apache.juneau.*;
39  import org.apache.juneau.annotation.*;
40  import org.apache.juneau.collections.*;
41  import org.apache.juneau.commons.collections.*;
42  import org.apache.juneau.commons.lang.*;
43  import org.apache.juneau.commons.reflect.*;
44  import org.apache.juneau.commons.utils.*;
45  import org.apache.juneau.http.annotation.*;
46  import org.apache.juneau.parser.*;
47  
48  /**
49   * Represents an OpenAPI schema definition.
50   *
51   * <p>
52   * The schema definition can be applied to any HTTP parts such as bodies, headers, query/form parameters, and URL path parts.
53   * <br>The API is generic enough to apply to any path part although some attributes may only applicable for certain parts.
54   *
55   * <p>
56   * Schema objects are created via builders instantiated through the {@link #create()} method.
57   *
58   * <h5 class='section'>Jakarta Bean Validation Support:</h5>
59   * <p>
60   * 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>).
61   * When these annotations are encountered during schema building, they are automatically mapped to corresponding OpenAPI schema properties:
62   * <ul>
63   * 	<li><c>@NotNull</c> → <c>required(true)</c>
64   * 	<li><c>@Size(min=x, max=y)</c> → <c>minLength/maxLength</c> and <c>minItems/maxItems</c>
65   * 	<li><c>@Min(value)</c> → <c>minimum(value)</c>
66   * 	<li><c>@Max(value)</c> → <c>maximum(value)</c>
67   * 	<li><c>@Pattern(regexp)</c> → <c>pattern(regexp)</c>
68   * 	<li><c>@Email</c> → <c>format("email")</c>
69   * 	<li><c>@Positive/@PositiveOrZero/@Negative/@NegativeOrZero</c> → Corresponding min/max constraints
70   * 	<li><c>@NotEmpty</c> → <c>required(true) + minLength(1)/minItems(1)</c>
71   * 	<li><c>@NotBlank</c> → <c>required(true) + minLength(1) + pattern</c>
72   * 	<li><c>@DecimalMin/@DecimalMax</c> → <c>minimum/maximum</c> with optional <c>exclusiveMinimum/exclusiveMaximum</c>
73   * </ul>
74   * <p>
75   * This integration uses pure reflection and does not require <c>jakarta.validation-api</c> as a dependency.
76   * The annotations are detected and processed automatically when present.
77   *
78   * <h5 class='section'>Notes:</h5><ul>
79   * 	<li class='note'>This class is thread safe and reusable.
80   * </ul>
81   *
82   * <h5 class='section'>See Also:</h5><ul>
83   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/OpenApiBasics">OpenApi Basics</a>
84   * </ul>
85   */
86  public class HttpPartSchema {
87  
88  	private static final AnnotationProvider AP = AnnotationProvider.INSTANCE;
89  
90  	/**
91  	 * Builder class.
92  	 */
93  	public static class Builder {
94  		String name, default_;
95  		Set<Integer> codes;
96  		Set<String> enum_;
97  		Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty;
98  		HttpPartCollectionFormat collectionFormat = HttpPartCollectionFormat.NO_COLLECTION_FORMAT;
99  		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 }