001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.httppart; 018 019import static java.util.Collections.*; 020import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 021import static org.apache.juneau.commons.utils.ClassUtils.*; 022import static org.apache.juneau.commons.utils.CollectionUtils.*; 023import static org.apache.juneau.commons.utils.StringUtils.*; 024import static org.apache.juneau.commons.utils.ThrowableUtils.*; 025import static org.apache.juneau.commons.utils.Utils.*; 026import static org.apache.juneau.Constants.*; 027import static org.apache.juneau.httppart.HttpPartDataType.*; 028import static org.apache.juneau.httppart.HttpPartFormat.*; 029 030import java.lang.annotation.*; 031import java.lang.reflect.*; 032import java.math.*; 033import java.util.*; 034import java.util.concurrent.atomic.*; 035import java.util.function.*; 036import java.util.regex.*; 037 038import org.apache.juneau.*; 039import org.apache.juneau.annotation.*; 040import org.apache.juneau.collections.*; 041import org.apache.juneau.commons.collections.*; 042import org.apache.juneau.commons.lang.*; 043import org.apache.juneau.commons.reflect.*; 044import org.apache.juneau.commons.utils.*; 045import org.apache.juneau.http.annotation.*; 046import org.apache.juneau.parser.*; 047 048/** 049 * Represents an OpenAPI schema definition. 050 * 051 * <p> 052 * The schema definition can be applied to any HTTP parts such as bodies, headers, query/form parameters, and URL path parts. 053 * <br>The API is generic enough to apply to any path part although some attributes may only applicable for certain parts. 054 * 055 * <p> 056 * Schema objects are created via builders instantiated through the {@link #create()} method. 057 * 058 * <h5 class='section'>Jakarta Bean Validation Support:</h5> 059 * <p> 060 * As of 9.2.0, this class supports Jakarta Bean Validation constraint annotations (e.g., <c>@NotNull</c>, <c>@Size</c>, <c>@Min</c>, <c>@Max</c>). 061 * When these annotations are encountered during schema building, they are automatically mapped to corresponding OpenAPI schema properties: 062 * <ul> 063 * <li><c>@NotNull</c> → <c>required(true)</c> 064 * <li><c>@Size(min=x, max=y)</c> → <c>minLength/maxLength</c> and <c>minItems/maxItems</c> 065 * <li><c>@Min(value)</c> → <c>minimum(value)</c> 066 * <li><c>@Max(value)</c> → <c>maximum(value)</c> 067 * <li><c>@Pattern(regexp)</c> → <c>pattern(regexp)</c> 068 * <li><c>@Email</c> → <c>format("email")</c> 069 * <li><c>@Positive/@PositiveOrZero/@Negative/@NegativeOrZero</c> → Corresponding min/max constraints 070 * <li><c>@NotEmpty</c> → <c>required(true) + minLength(1)/minItems(1)</c> 071 * <li><c>@NotBlank</c> → <c>required(true) + minLength(1) + pattern</c> 072 * <li><c>@DecimalMin/@DecimalMax</c> → <c>minimum/maximum</c> with optional <c>exclusiveMinimum/exclusiveMaximum</c> 073 * </ul> 074 * <p> 075 * This integration uses pure reflection and does not require <c>jakarta.validation-api</c> as a dependency. 076 * The annotations are detected and processed automatically when present. 077 * 078 * <h5 class='section'>Notes:</h5><ul> 079 * <li class='note'>This class is thread safe and reusable. 080 * </ul> 081 * 082 * <h5 class='section'>See Also:</h5><ul> 083 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/OpenApiBasics">OpenApi Basics</a> 084 * </ul> 085 */ 086public class HttpPartSchema { 087 088 private static final AnnotationProvider AP = AnnotationProvider.INSTANCE; 089 090 /** 091 * Builder class. 092 */ 093 public static class Builder { 094 String name, default_; 095 Set<Integer> codes; 096 Set<String> enum_; 097 Boolean allowEmptyValue, exclusiveMaximum, exclusiveMinimum, required, uniqueItems, skipIfEmpty; 098 HttpPartCollectionFormat collectionFormat = HttpPartCollectionFormat.NO_COLLECTION_FORMAT; 099 HttpPartDataType type = HttpPartDataType.NO_TYPE; 100 HttpPartFormat format = HttpPartFormat.NO_FORMAT; 101 Pattern pattern; 102 Number maximum, minimum, multipleOf; 103 Long maxLength, minLength, maxItems, minItems, maxProperties, minProperties; 104 Map<String,Object> properties; 105 Object items, additionalProperties; 106 boolean noValidate; 107 Class<? extends HttpPartParser> parser; 108 Class<? extends HttpPartSerializer> serializer; 109 // JSON Schema Draft 2020-12 properties 110 String const_; 111 String[] examples; 112 Boolean deprecated; 113 Number exclusiveMaximumValue, exclusiveMinimumValue; 114 115 /** 116 * <mk>const</mk> field (JSON Schema Draft 2020-12). 117 * 118 * <p> 119 * Defines a constant value for this schema. 120 * The instance must be equal to this value to validate. 121 * 122 * @param value 123 * The new value for this property. 124 * @return This object. 125 */ 126 public Builder const_(String value) { 127 const_ = value; 128 return this; 129 } 130 131 /** 132 * <mk>default</mk> field. 133 * 134 * <p> 135 * Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request. 136 * <br>(Note: "default" has no meaning for required parameters.) 137 * 138 * <p> 139 * Applicable to the following Swagger schema objects: 140 * <ul> 141 * <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a> 142 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 143 * <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a> 144 * <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a> 145 * </ul> 146 * 147 * @param value 148 * The new value for this property. 149 * <br>Ignored if value is <jk>null</jk>. 150 * @return This object. 151 */ 152 public Builder default_(String value) { 153 if (ne(value)) 154 default_ = value; 155 return this; 156 } 157 158 /** 159 * <mk>enum</mk> field. 160 * 161 * <p> 162 * If specified, the input validates successfully if it is equal to one of the elements in this array. 163 * 164 * <p> 165 * Applicable to the following Swagger schema objects: 166 * <ul> 167 * <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a> 168 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 169 * <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a> 170 * <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a> 171 * </ul> 172 * 173 * @param value 174 * The new value for this property. 175 * <br>Ignored if value is <jk>null</jk> or an empty set. 176 * @return This object. 177 */ 178 public Builder enum_(Set<String> value) { 179 if (nn(value) && ! value.isEmpty()) 180 enum_ = value; 181 return this; 182 } 183 184 /** 185 * <mk>enum</mk> field. 186 * 187 * <p> 188 * Same as {@link #enum_(Set)} but takes in a var-args array. 189 * 190 * @param values 191 * The new values for this property. 192 * <br>Ignored if value is empty. 193 * @return This object. 194 */ 195 public Builder enum_(String...values) { 196 return enum_(set(values)); 197 } 198 199 /** 200 * <mk>additionalProperties</mk> field. 201 * 202 * <p> 203 * Applicable to the following Swagger schema objects: 204 * <ul> 205 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 206 * </ul> 207 * 208 * @param value 209 * The new value for this property. 210 * <br>Ignored if value is <jk>null</jk> or empty. 211 * @return This object. 212 */ 213 public Builder additionalProperties(Builder value) { 214 if (nn(value)) 215 additionalProperties = value; 216 return this; 217 } 218 219 /** 220 * <mk>additionalProperties</mk> field. 221 * 222 * <p> 223 * Applicable to the following Swagger schema objects: 224 * <ul> 225 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 226 * </ul> 227 * 228 * @param value 229 * The new value for this property. 230 * <br>Ignored if value is <jk>null</jk> or empty. 231 * @return This object. 232 */ 233 public Builder additionalProperties(HttpPartSchema value) { 234 if (nn(value)) 235 additionalProperties = value; 236 return this; 237 } 238 239 /** 240 * Synonym for {@link #allowEmptyValue()}. 241 * 242 * @return This object. 243 */ 244 public Builder aev() { 245 return allowEmptyValue(true); 246 } 247 248 /** 249 * Synonym for {@link #allowEmptyValue(Boolean)}. 250 * 251 * @param value 252 * The new value for this property. 253 * @return This object. 254 */ 255 public Builder aev(Boolean value) { 256 return allowEmptyValue(value); 257 } 258 259 /** 260 * Synonym for {@link #allowEmptyValue(String)}. 261 * 262 * @param value 263 * The new value for this property. 264 * @return This object. 265 */ 266 public Builder aev(String value) { 267 return allowEmptyValue(value); 268 } 269 270 /** 271 * <mk>allowEmptyValue</mk> field. 272 * 273 * <p> 274 * Shortcut for calling <code>allowEmptyValue(<jk>true</jk>);</code>. 275 * 276 * @return This object. 277 */ 278 public Builder allowEmptyValue() { 279 return allowEmptyValue(true); 280 } 281 282 /** 283 * <mk>allowEmptyValue</mk> field. 284 * 285 * <p> 286 * Sets the ability to pass empty-valued parameters. 287 * <br>This is valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value. 288 * <br>The default value is <jk>false</jk>. 289 * 290 * <p> 291 * Applicable to the following Swagger schema objects: 292 * <ul> 293 * <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a> 294 * </ul> 295 * 296 * @param value 297 * The new value for this property. 298 * <br>Ignored if value is <jk>null</jk>. 299 * @return This object. 300 */ 301 public Builder allowEmptyValue(Boolean value) { 302 allowEmptyValue = resolve(value, allowEmptyValue); 303 return this; 304 } 305 306 /** 307 * <mk>allowEmptyValue</mk> field. 308 * 309 * <p> 310 * Same as {@link #allowEmptyValue(Boolean)} but takes in a string boolean value. 311 * 312 * @param value 313 * The new value for this property. 314 * <br>Ignored if value is <jk>null</jk> or empty. 315 * @return This object. 316 */ 317 public Builder allowEmptyValue(String value) { 318 allowEmptyValue = resolve(value, allowEmptyValue); 319 return this; 320 } 321 322 /** 323 * Shortcut for <c>additionalProperties(value)</c> 324 * 325 * <p> 326 * Applicable to the following Swagger schema objects: 327 * <ul> 328 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 329 * </ul> 330 * 331 * @param value 332 * The new value for this property. 333 * <br>Ignored if value is <jk>null</jk> or empty. 334 * @return This object. 335 */ 336 public Builder ap(Builder value) { 337 return additionalProperties(value); 338 } 339 340 /** 341 * Shortcut for <c>additionalProperties(value)</c> 342 * 343 * <p> 344 * Applicable to the following Swagger schema objects: 345 * <ul> 346 * <li><a class="doclink" href="https://swagger.io/specification/v2#schemaObject">Schema</a> 347 * </ul> 348 * 349 * @param value 350 * The new value for this property. 351 * <br>Ignored if value is <jk>null</jk> or empty. 352 * @return This object. 353 */ 354 public Builder ap(HttpPartSchema value) { 355 return additionalProperties(value); 356 } 357 358 /** 359 * Apply the specified annotation to this schema. 360 * 361 * @param a The annotation to apply. 362 * @return This object. 363 */ 364 public Builder apply(Annotation a) { 365 if (a instanceof Content a2) 366 apply(a2); 367 else if (a instanceof Header a2) 368 apply(a2); 369 else if (a instanceof FormData a2) 370 apply(a2); 371 else if (a instanceof Query a3) 372 apply(a3); 373 else if (a instanceof Path a4) 374 apply(a4); 375 else if (a instanceof PathRemainder a5) 376 apply(a5); 377 else if (a instanceof Response a6) 378 apply(a6); 379 else if (a instanceof StatusCode a7) 380 apply(a7); 381 else if (a instanceof HasQuery a8) 382 apply(a8); 383 else if (a instanceof HasFormData a9) 384 apply(a9); 385 else if (a instanceof Schema a10) 386 apply(a10); 387 else if (cn(a.annotationType()).startsWith("jakarta.validation.constraints.")) 388 applyJakartaValidation(a); 389 else 390 throw rex("Builder.apply(@{0}) not defined", cn(a)); 391 return this; 392 } 393 394 /** 395 * Instantiates a new {@link HttpPartSchema} object based on the configuration of this builder. 396 * 397 * <p> 398 * This method can be called multiple times to produce new schema objects. 399 * 400 * @return 401 * A new {@link HttpPartSchema} object. 402 * <br>Never <jk>null</jk>. 403 */ 404 public HttpPartSchema build() { 405 return new HttpPartSchema(this); 406 } 407 408 /** 409 * Synonym for {@link #collectionFormat(HttpPartCollectionFormat)}. 410 * 411 * @param value 412 * The new value for this property. 413 * @return This object. 414 */ 415 public Builder cf(HttpPartCollectionFormat value) { 416 return collectionFormat(value); 417 } 418 419 /** 420 * Synonym for {@link #collectionFormat(String)}. 421 * 422 * @param value 423 * The new value for this property. 424 * @return This object. 425 */ 426 public Builder cf(String value) { 427 return collectionFormat(value); 428 } 429 430 /** 431 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.CSV)</c>. 432 * 433 * @return This object. 434 */ 435 public Builder cfCsv() { 436 return collectionFormat(HttpPartCollectionFormat.CSV); 437 } 438 439 /** 440 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.MULTI)</c>. 441 * 442 * @return This object. 443 */ 444 public Builder cfMulti() { 445 return collectionFormat(HttpPartCollectionFormat.MULTI); 446 } 447 448 /** 449 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT)</c>. 450 * 451 * @return This object. 452 */ 453 public Builder cfNone() { 454 return collectionFormat(HttpPartCollectionFormat.NO_COLLECTION_FORMAT); 455 } 456 457 /** 458 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.PIPES)</c>. 459 * 460 * @return This object. 461 */ 462 public Builder cfPipes() { 463 return collectionFormat(HttpPartCollectionFormat.PIPES); 464 } 465 466 /** 467 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.SSV)</c>. 468 * 469 * @return This object. 470 */ 471 public Builder cfSsv() { 472 return collectionFormat(HttpPartCollectionFormat.SSV); 473 } 474 475 /** 476 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.TSV)</c>. 477 * 478 * @return This object. 479 */ 480 public Builder cfTsv() { 481 return collectionFormat(HttpPartCollectionFormat.TSV); 482 } 483 484 /** 485 * Shortcut for <c>collectionFormat(HttpPartCollectionFormat.UONC)</c>. 486 * 487 * @return This object. 488 */ 489 public Builder cfUon() { 490 return collectionFormat(HttpPartCollectionFormat.UONC); 491 } 492 493 /** 494 * <mk>httpStatusCode</mk> key. 495 * 496 * <p> 497 * Applicable to the following Swagger schema objects: 498 * <ul> 499 * <li><a class="doclink" href="https://swagger.io/specification/v2#responsesObject">Responses</a> 500 * </ul> 501 * 502 * @param value 503 * The new value for this property. 504 * <br>Ignored if value is <c>0</c>. 505 * @return This object. 506 */ 507 public Builder code(int value) { 508 if (value != 0) { 509 if (codes == null) 510 codes = new TreeSet<>(); 511 codes.add(value); 512 } 513 return this; 514 } 515 516 /** 517 * <mk>httpStatusCode</mk> key. 518 * 519 * <p> 520 * Applicable to the following Swagger schema objects: 521 * <ul> 522 * <li><a class="doclink" href="https://swagger.io/specification/v2#responsesObject">Responses</a> 523 * </ul> 524 * 525 * @param value 526 * The new value for this property. 527 * <br>Ignored if <jk>null</jk> or an empty array. 528 * @return This object. 529 */ 530 public Builder codes(int[] value) { 531 if (nn(value) && value.length != 0) 532 for (var v : value) 533 code(v); 534 return this; 535 } 536 537 /** 538 * <mk>collectionFormat</mk> field. 539 * 540 * <p> 541 * Determines the format of the array if <c>type</c> <js>"array"</js> is used. 542 * <br>Can only be used if <c>type</c> is <js>"array"</js>. 543 * 544 * <p> 545 * Applicable to the following Swagger schema objects: 546 * <ul> 547 * <li><a class="doclink" href="https://swagger.io/specification/v2#parameterObject">Parameter</a> 548 * <li><a class="doclink" href="https://swagger.io/specification/v2#itemsObject">Items</a> 549 * <li><a class="doclink" href="https://swagger.io/specification/v2#headerObject">Header</a> 550 * </ul> 551 * 552 * <p> 553 * Note that for collections/arrays parameters with POJO element types, the input is broken into a string array before being converted into POJO elements. 554 * 555 * <ul class='values javatree'> 556 * <ul class='jc'>{@link HttpPartCollectionFormat} 557 * <ul> 558 * <li> 559 * {@link HttpPartCollectionFormat#CSV CSV} (default) - Comma-separated values (e.g. <js>"foo,bar"</js>). 560 * <li> 561 * {@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. <js>"foo bar"</js>). 562 * <li> 563 * {@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. <js>"foo\tbar"</js>). 564 * <li> 565 * {@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. <js>"foo|bar"</js>). 566 * <li> 567 * {@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&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}