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&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&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 }