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.bean.openapi3; 018 019import static org.apache.juneau.common.utils.Utils.*; 020import static org.apache.juneau.internal.ArrayUtils.contains; 021import static org.apache.juneau.internal.CollectionUtils.*; 022import static org.apache.juneau.internal.CollectionUtils.copyOf; 023import static org.apache.juneau.internal.ConverterUtils.*; 024 025import java.util.*; 026 027import org.apache.juneau.*; 028import org.apache.juneau.annotation.*; 029import org.apache.juneau.collections.*; 030import org.apache.juneau.common.utils.*; 031import org.apache.juneau.internal.*; 032import org.apache.juneau.json.*; 033 034/** 035 * A limited subset of JSON-Schema's items object. 036 * 037 * <p> 038 * The Items Object is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are 039 * not located in "body" to describe the type of items in an array. This is particularly useful for query parameters, 040 * path parameters, and header parameters that accept arrays. 041 * 042 * <h5 class='section'>OpenAPI Specification:</h5> 043 * <p> 044 * The Items Object supports the following fields from JSON Schema: 045 * <ul class='spaced-list'> 046 * <li><c>type</c> (string, REQUIRED) - The data type. Values: <js>"string"</js>, <js>"number"</js>, <js>"integer"</js>, <js>"boolean"</js>, <js>"array"</js> 047 * <li><c>format</c> (string) - The format modifier (e.g., <js>"int32"</js>, <js>"int64"</js>, <js>"float"</js>, <js>"double"</js>, <js>"date"</js>, <js>"date-time"</js>) 048 * <li><c>items</c> ({@link Items}) - Required if type is <js>"array"</js>. Describes the type of items in the array 049 * <li><c>collectionFormat</c> (string) - How multiple values are formatted. Values: <js>"csv"</js>, <js>"ssv"</js>, <js>"tsv"</js>, <js>"pipes"</js>, <js>"multi"</js> 050 * <li><c>default</c> (any) - The default value 051 * <li><c>maximum</c> (number), <c>exclusiveMaximum</c> (boolean), <c>minimum</c> (number), <c>exclusiveMinimum</c> (boolean) - Numeric constraints 052 * <li><c>maxLength</c> (integer), <c>minLength</c> (integer), <c>pattern</c> (string) - String constraints 053 * <li><c>maxItems</c> (integer), <c>minItems</c> (integer), <c>uniqueItems</c> (boolean) - Array constraints 054 * <li><c>enum</c> (array) - Possible values for this item 055 * <li><c>multipleOf</c> (number) - Must be a multiple of this value 056 * </ul> 057 * 058 * <h5 class='section'>Example:</h5> 059 * <p class='bcode'> 060 * <jc>// Construct using SwaggerBuilder.</jc> 061 * Items <jv>x</jv> = <jsm>items</jsm>(<js>"string"</js>).minLength(2); 062 * 063 * <jc>// Serialize using JsonSerializer.</jc> 064 * String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>); 065 * 066 * <jc>// Or just use toString() which does the same as above.</jc> 067 * String <jv>json</jv> = <jv>x</jv>.toString(); 068 * </p> 069 * <p class='bcode'> 070 * <jc>// Output</jc> 071 * { 072 * <js>"type"</js>: <js>"string"</js>, 073 * <js>"minLength"</js>: 2 074 * } 075 * </p> 076 * 077 * <h5 class='section'>See Also:</h5><ul> 078 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#items-object">OpenAPI Specification > Items Object</a> 079 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a> 080 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a> 081 * </ul> 082 */ 083public class Items extends OpenApiElement { 084 085 private static final String[] VALID_TYPES = {"string", "number", "integer", "boolean", "array"}; 086 private static final String[] VALID_COLLECTION_FORMATS = {"csv","ssv","tsv","pipes","multi"}; 087 088 private String 089 type, 090 format, 091 collectionFormat, 092 pattern, 093 ref; 094 private Number 095 maximum, 096 minimum, 097 multipleOf; 098 private Integer 099 maxLength, 100 minLength, 101 maxItems, 102 minItems; 103 private Boolean 104 exclusiveMaximum, 105 exclusiveMinimum, 106 uniqueItems; 107 private Items items; // NOSONAR - Intentional naming. 108 private Object _default; // NOSONAR - Intentional naming. 109 private List<Object> _enum; // NOSONAR - Intentional naming. 110 111 /** 112 * Default constructor. 113 */ 114 public Items() {} 115 116 /** 117 * Copy constructor. 118 * 119 * @param copyFrom The object to copy. 120 */ 121 public Items(Items copyFrom) { 122 super(copyFrom); 123 124 this.type = copyFrom.type; 125 this.format = copyFrom.format; 126 this.collectionFormat = copyFrom.collectionFormat; 127 this.pattern = copyFrom.pattern; 128 this.maximum = copyFrom.maximum; 129 this.minimum = copyFrom.minimum; 130 this.multipleOf = copyFrom.multipleOf; 131 this.maxLength = copyFrom.maxLength; 132 this.minLength = copyFrom.minLength; 133 this.maxItems = copyFrom.maxItems; 134 this.minItems = copyFrom.minItems; 135 this.exclusiveMaximum = copyFrom.exclusiveMaximum; 136 this.exclusiveMinimum = copyFrom.exclusiveMinimum; 137 this.uniqueItems = copyFrom.uniqueItems; 138 this.items = copyFrom.items == null ? null : copyFrom.items.copy(); 139 this._default = copyFrom._default; 140 this._enum = copyOf(copyFrom._enum); 141 this.ref = copyFrom.ref; 142 } 143 144 /** 145 * Make a deep copy of this object. 146 * 147 * @return A deep copy of this object. 148 */ 149 public Items copy() { 150 return new Items(this); 151 } 152 153 @Override /* SwaggerElement */ 154 protected Items strict() { 155 super.strict(); 156 return this; 157 } 158 159 @Override /* Overridden from OpenApiElement */ 160 public Items strict(Object value) { 161 super.strict(value); 162 return this; 163 } 164 165 /** 166 * Bean property getter: <property>type</property>. 167 * 168 * <p> 169 * The internal type of the array. 170 * 171 * @return The property value, or <jk>null</jk> if it is not set. 172 */ 173 public String getType() { 174 return type; 175 } 176 177 /** 178 * Bean property setter: <property>type</property>. 179 * 180 * <p> 181 * The internal type of the array. 182 * 183 * @param value 184 * The new value for this property. 185 * <br>Valid values: 186 * <ul> 187 * <li><js>"string"</js> 188 * <li><js>"number"</js> 189 * <li><js>"integer"</js> 190 * <li><js>"boolean"</js> 191 * <li><js>"array"</js> 192 * </ul> 193 * <br>Property value is required. 194 * <br>Can be <jk>null</jk> to unset the property. 195 * @return This object 196 */ 197 public Items setType(String value) { 198 if (isStrict() && ! contains(value, VALID_TYPES)) 199 throw new IllegalArgumentException( 200 "Invalid value passed in to setType(String). Value='"+value+"', valid values=" 201 + Json5Serializer.DEFAULT.toString(VALID_TYPES)); 202 type = value; 203 return this; 204 } 205 206 /** 207 * Bean property getter: <property>format</property>. 208 * 209 * <p> 210 * The extending format for the previously mentioned <code>type</code>. 211 * 212 * @return The property value, or <jk>null</jk> if it is not set. 213 */ 214 public String getFormat() { 215 return format; 216 } 217 218 /** 219 * Bean property setter: <property>format</property>. 220 * 221 * <p> 222 * The extending format for the previously mentioned <code>type</code>. 223 * 224 * @param value 225 * The new value for this property. 226 * <br>Can be <jk>null</jk> to unset the property. 227 * @return This object 228 */ 229 public Items setFormat(String value) { 230 format = value; 231 return this; 232 } 233 234 /** 235 * Bean property getter: <property>items</property>. 236 * 237 * <p> 238 * Describes the type of items in the array. 239 * 240 * @return The property value, or <jk>null</jk> if it is not set. 241 */ 242 public Items getItems() { 243 return items; 244 } 245 246 /** 247 * Bean property setter: <property>items</property>. 248 * 249 * <p> 250 * Describes the type of items in the array. 251 * 252 * @param value 253 * The new value for this property. 254 * <br>Property value is required if <code>type</code> is <js>"array"</js>. 255 * <br>Can be <jk>null</jk> to unset the property. 256 * @return This object 257 */ 258 public Items setItems(Items value) { 259 items = value; 260 return this; 261 } 262 263 /** 264 * Bean property getter: <property>collectionFormat</property>. 265 * 266 * <p> 267 * Determines the format of the array if type array is used. 268 * 269 * @return The property value, or <jk>null</jk> if it is not set. 270 */ 271 public String getCollectionFormat() { 272 return collectionFormat; 273 } 274 275 /** 276 * Bean property setter: <property>collectionFormat</property>. 277 * 278 * <p> 279 * Determines the format of the array if type array is used. 280 * 281 * @param value 282 * The new value for this property. 283 * <br>Valid values: 284 * <ul> 285 * <li><js>"csv"</js> (default) - comma separated values <code>foo,bar</code>. 286 * <li><js>"ssv"</js> - space separated values <code>foo bar</code>. 287 * <li><js>"tsv"</js> - tab separated values <code>foo\tbar</code>. 288 * <li><js>"pipes"</js> - pipe separated values <code>foo|bar</code>. 289 * </ul> 290 * <br>Can be <jk>null</jk> to unset the property. 291 * @return This object 292 */ 293 public Items setCollectionFormat(String value) { 294 if (isStrict() && ! contains(value, VALID_COLLECTION_FORMATS)) 295 throw new BasicRuntimeException( 296 "Invalid value passed in to setCollectionFormat(String). Value=''{0}'', valid values={1}", 297 value, VALID_COLLECTION_FORMATS 298 ); 299 collectionFormat = value; 300 return this; 301 } 302 303 /** 304 * Bean property getter: <property>default</property>. 305 * 306 * <p> 307 * Declares the value of the item that the server will use if none is provided. 308 * 309 * <h5 class='section'>Notes:</h5> 310 * <ul class='spaced-list'> 311 * <li> 312 * <js>"default"</js> has no meaning for required items. 313 * <li> 314 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for the data type. 315 * </ul> 316 * 317 * @return The property value, or <jk>null</jk> if it is not set. 318 */ 319 public Object getDefault() { 320 return _default; 321 } 322 323 /** 324 * Bean property setter: <property>default</property>. 325 * 326 * <p> 327 * Declares the value of the item that the server will use if none is provided. 328 * 329 * <h5 class='section'>Notes:</h5> 330 * <ul class='spaced-list'> 331 * <li> 332 * <js>"default"</js> has no meaning for required items. 333 * <li> 334 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for the data type. 335 * </ul> 336 * 337 * @param value 338 * The new value for this property. 339 * <br>Can be <jk>null</jk> to unset the property. 340 * @return This object 341 */ 342 public Items setDefault(Object value) { 343 _default = value; 344 return this; 345 } 346 347 /** 348 * Bean property getter: <property>maximum</property>. 349 * 350 * @return The property value, or <jk>null</jk> if it is not set. 351 */ 352 public Number getMaximum() { 353 return maximum; 354 } 355 356 /** 357 * Bean property setter: <property>maximum</property>. 358 * 359 * @param value 360 * The new value for this property. 361 * <br>Can be <jk>null</jk> to unset the property. 362 * @return This object 363 */ 364 public Items setMaximum(Number value) { 365 maximum = value; 366 return this; 367 } 368 369 /** 370 * Bean property getter: <property>exclusiveMaximum</property>. 371 * 372 * @return The property value, or <jk>null</jk> if it is not set. 373 */ 374 public Boolean getExclusiveMaximum() { 375 return exclusiveMaximum; 376 } 377 378 /** 379 * Bean property setter: <property>exclusiveMaximum</property>. 380 * 381 * @param value 382 * The new value for this property. 383 * <br>Can be <jk>null</jk> to unset the property. 384 * @return This object 385 */ 386 public Items setExclusiveMaximum(Boolean value) { 387 exclusiveMaximum = value; 388 return this; 389 } 390 391 /** 392 * Bean property getter: <property>minimum</property>. 393 * 394 * @return The property value, or <jk>null</jk> if it is not set. 395 */ 396 public Number getMinimum() { 397 return minimum; 398 } 399 400 /** 401 * Bean property setter: <property>minimum</property>. 402 * 403 * @param value 404 * The new value for this property. 405 * <br>Can be <jk>null</jk> to unset the property. 406 * @return This object 407 */ 408 public Items setMinimum(Number value) { 409 minimum = value; 410 return this; 411 } 412 413 /** 414 * Bean property getter: <property>exclusiveMinimum</property>. 415 * 416 * @return The property value, or <jk>null</jk> if it is not set. 417 */ 418 public Boolean getExclusiveMinimum() { 419 return exclusiveMinimum; 420 } 421 422 /** 423 * Bean property setter: <property>exclusiveMinimum</property>. 424 * 425 * @param value 426 * The new value for this property. 427 * <br>Can be <jk>null</jk> to unset the property. 428 * @return This object 429 */ 430 public Items setExclusiveMinimum(Boolean value) { 431 exclusiveMinimum = value; 432 return this; 433 } 434 435 /** 436 * Bean property getter: <property>maxLength</property>. 437 * 438 * @return The property value, or <jk>null</jk> if it is not set. 439 */ 440 public Integer getMaxLength() { 441 return maxLength; 442 } 443 444 /** 445 * Bean property setter: <property>maxLength</property>. 446 * 447 * @param value 448 * The new value for this property. 449 * <br>Can be <jk>null</jk> to unset the property. 450 * @return This object 451 */ 452 public Items setMaxLength(Integer value) { 453 maxLength = value; 454 return this; 455 } 456 457 /** 458 * Bean property getter: <property>minLength</property>. 459 * 460 * @return The property value, or <jk>null</jk> if it is not set. 461 */ 462 public Integer getMinLength() { 463 return minLength; 464 } 465 466 /** 467 * Bean property setter: <property>minLength</property>. 468 * 469 * @param value 470 * The new value for this property. 471 * <br>Can be <jk>null</jk> to unset the property. 472 * @return This object 473 */ 474 public Items setMinLength(Integer value) { 475 minLength = value; 476 return this; 477 } 478 479 /** 480 * Bean property getter: <property>pattern</property>. 481 * 482 * @return The property value, or <jk>null</jk> if it is not set. 483 */ 484 public String getPattern() { 485 return pattern; 486 } 487 488 /** 489 * Bean property setter: <property>pattern</property>. 490 * 491 * <p> 492 * This string SHOULD be a valid regular expression. 493 * 494 * @param value 495 * The new value for this property. 496 * <br>Can be <jk>null</jk> to unset the property. 497 * @return This object 498 */ 499 public Items setPattern(String value) { 500 pattern = value; 501 return this; 502 } 503 504 /** 505 * Bean property getter: <property>maxItems</property>. 506 * 507 * @return The property value, or <jk>null</jk> if it is not set. 508 */ 509 public Integer getMaxItems() { 510 return maxItems; 511 } 512 513 /** 514 * Bean property setter: <property>maxItems</property>. 515 * 516 * @param value 517 * The new value for this property. 518 * <br>Can be <jk>null</jk> to unset the property. 519 * @return This object 520 */ 521 public Items setMaxItems(Integer value) { 522 maxItems = value; 523 return this; 524 } 525 526 /** 527 * Bean property getter: <property>minItems</property>. 528 * 529 * @return The property value, or <jk>null</jk> if it is not set. 530 */ 531 public Integer getMinItems() { 532 return minItems; 533 } 534 535 /** 536 * Bean property setter: <property>minItems</property>. 537 * 538 * @param value 539 * The new value for this property. 540 * <br>Can be <jk>null</jk> to unset the property. 541 * @return This object 542 */ 543 public Items setMinItems(Integer value) { 544 minItems = value; 545 return this; 546 } 547 548 /** 549 * Bean property getter: <property>uniqueItems</property>. 550 * 551 * @return The property value, or <jk>null</jk> if it is not set. 552 */ 553 public Boolean getUniqueItems() { 554 return uniqueItems; 555 } 556 557 /** 558 * Bean property setter: <property>uniqueItems</property>. 559 * 560 * @param value 561 * The new value for this property. 562 * <br>Can be <jk>null</jk> to unset the property. 563 * @return This object 564 */ 565 public Items setUniqueItems(Boolean value) { 566 uniqueItems = value; 567 return this; 568 } 569 570 /** 571 * Bean property getter: <property>enum</property>. 572 * 573 * @return The property value, or <jk>null</jk> if it is not set. 574 */ 575 public List<Object> getEnum() { 576 return _enum; 577 } 578 579 /** 580 * Bean property setter: <property>enum</property>. 581 * 582 * @param value 583 * The new value for this property. 584 * <br>Can be <jk>null</jk> to unset the property. 585 * @return This object 586 */ 587 public Items setEnum(Collection<Object> value) { 588 _enum = listFrom(value); 589 return this; 590 } 591 592 /** 593 * Adds one or more values to the <property>enum</property> property. 594 * 595 * @param values 596 * The values to add to this property. 597 * <br>Ignored if <jk>null</jk>. 598 * @return This object 599 */ 600 public Items addEnum(Object...values) { 601 _enum = listBuilder(_enum).elementType(Object.class).sparse().addAny(values).build(); 602 return this; 603 } 604 605 /** 606 * Adds one or more values to the <property>enum</property> property. 607 * 608 * @param values 609 * The values to add to this property. 610 * <br>Valid types: 611 * <ul> 612 * <li><code>Object</code> 613 * <li><code>Collection<Object></code> 614 * <li><code>String</code> - JSON array representation of <code>Collection<Object></code> 615 * <h5 class='figure'>Example:</h5> 616 * <p class='bcode'> 617 * _enum(<js>"['foo','bar']"</js>); 618 * </p> 619 * <li><code>String</code> - Individual values 620 * <h5 class='figure'>Example:</h5> 621 * <p class='bcode'> 622 * _enum(<js>"foo"</js>, <js>"bar"</js>); 623 * </p> 624 * </ul> 625 * <br>Ignored if <jk>null</jk>. 626 * @return This object 627 */ 628 public Items setEnum(Object...values) { // NOSONAR - Intentional naming. 629 _enum = listBuilder(_enum).elementType(Object.class).sparse().addAny(values).build(); 630 return this; 631 } 632 633 /** 634 * Bean property getter: <property>multipleOf</property>. 635 * 636 * @return The property value, or <jk>null</jk> if it is not set. 637 */ 638 public Number getMultipleOf() { 639 return multipleOf; 640 } 641 642 /** 643 * Bean property setter: <property>multipleOf</property>. 644 * 645 * @param value 646 * The new value for this property. 647 * <br>Can be <jk>null</jk> to unset the property. 648 * @return This object 649 */ 650 public Items setMultipleOf(Number value) { 651 multipleOf = value; 652 return this; 653 } 654 655 /** 656 * Bean property getter: <property>$ref</property>. 657 * 658 * @return The property value, or <jk>null</jk> if it is not set. 659 */ 660 @Beanp("$ref") 661 public String getRef() { 662 return ref; 663 } 664 665 /** 666 * Bean property setter: <property>$ref</property>. 667 * 668 * @param value 669 * The new value for this property. 670 * <br>Can be <jk>null</jk> to unset the property. 671 * @return This object 672 */ 673 @Beanp("$ref") 674 public Items setRef(Object value) { 675 ref = Utils.s(value); 676 return this; 677 } 678 679 @Override /* SwaggerElement */ 680 public <T> T get(String property, Class<T> type) { 681 assertArgNotNull("property", property); 682 return switch (property) { 683 case "type" -> toType(getType(), type); 684 case "format" -> toType(getFormat(), type); 685 case "items" -> toType(getItems(), type); 686 case "collectionFormat" -> toType(getCollectionFormat(), type); 687 case "default" -> toType(getDefault(), type); 688 case "maximum" -> toType(getMaximum(), type); 689 case "exclusiveMaximum" -> toType(getExclusiveMaximum(), type); 690 case "minimum" -> toType(getMinimum(), type); 691 case "exclusiveMinimum" -> toType(getExclusiveMinimum(), type); 692 case "maxLength" -> toType(getMaxLength(), type); 693 case "minLength" -> toType(getMinLength(), type); 694 case "pattern" -> toType(getPattern(), type); 695 case "maxItems" -> toType(getMaxItems(), type); 696 case "minItems" -> toType(getMinItems(), type); 697 case "uniqueItems" -> toType(getUniqueItems(), type); 698 case "enum" -> toType(getEnum(), type); 699 case "multipleOf" -> toType(getMultipleOf(), type); 700 case "$ref" -> toType(getRef(), type); 701 default -> super.get(property, type); 702 }; 703 } 704 705 @Override /* SwaggerElement */ 706 public Items set(String property, Object value) { 707 assertArgNotNull("property", property); 708 return switch (property) { 709 case "$ref" -> setRef(value); 710 case "collectionFormat" -> setCollectionFormat(Utils.s(value)); 711 case "default" -> setDefault(value); 712 case "enum" -> setEnum(value); 713 case "exclusiveMaximum" -> setExclusiveMaximum(toBoolean(value)); 714 case "exclusiveMinimum" -> setExclusiveMinimum(toBoolean(value)); 715 case "format" -> setFormat(Utils.s(value)); 716 case "items" -> setItems(toType(value, Items.class)); 717 case "maxItems" -> setMaxItems(toInteger(value)); 718 case "maxLength" -> setMaxLength(toInteger(value)); 719 case "maximum" -> setMaximum(toNumber(value)); 720 case "minItems" -> setMinItems(toInteger(value)); 721 case "minLength" -> setMinLength(toInteger(value)); 722 case "minimum" -> setMinimum(toNumber(value)); 723 case "multipleOf" -> setMultipleOf(toNumber(value)); 724 case "pattern" -> setPattern(Utils.s(value)); 725 case "type" -> setType(Utils.s(value)); 726 case "uniqueItems" -> setUniqueItems(toBoolean(value)); 727 default -> { 728 super.set(property, value); 729 yield this; 730 } 731 }; 732 } 733 734 @Override /* SwaggerElement */ 735 public Set<String> keySet() { 736 var s = setBuilder(String.class) 737 .addIf(ref != null, "$ref") 738 .addIf(collectionFormat != null, "collectionFormat") 739 .addIf(_default != null, "default") 740 .addIf(_enum != null, "enum") 741 .addIf(exclusiveMaximum != null, "exclusiveMaximum") 742 .addIf(exclusiveMinimum != null, "exclusiveMinimum") 743 .addIf(format != null, "format") 744 .addIf(items != null, "items") 745 .addIf(maxItems != null, "maxItems") 746 .addIf(maxLength != null, "maxLength") 747 .addIf(maximum != null, "maximum") 748 .addIf(minItems != null, "minItems") 749 .addIf(minLength != null, "minLength") 750 .addIf(minimum != null, "minimum") 751 .addIf(multipleOf != null, "multipleOf") 752 .addIf(pattern != null, "pattern") 753 .addIf(type != null, "type") 754 .addIf(uniqueItems != null, "uniqueItems") 755 .build(); 756 return new MultiSet<>(s, super.keySet()); 757 } 758 759 /** 760 * Resolves any <js>"$ref"</js> attributes in this element. 761 * 762 * @param openApi The swagger document containing the definitions. 763 * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. 764 * @param maxDepth 765 * The maximum depth to resolve references. 766 * <br>After that level is reached, <code>$ref</code> references will be left alone. 767 * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. 768 * @return 769 * This object with references resolved. 770 * <br>May or may not be the same object. 771 */ 772 public Items resolveRefs(OpenApi openApi, Deque<String> refStack, int maxDepth) { 773 774 if (ref != null) { 775 if (refStack.contains(ref) || refStack.size() >= maxDepth) 776 return this; 777 refStack.addLast(ref); 778 var r = openApi.findRef(ref, Items.class); 779 r = r.resolveRefs(openApi, refStack, maxDepth); 780 refStack.removeLast(); 781 return r; 782 } 783 784 set("properties", resolveRefs(get("properties"), openApi, refStack, maxDepth)); 785 786 if (items != null) 787 items = items.resolveRefs(openApi, refStack, maxDepth); 788 789 set("example", null); 790 791 return this; 792 } 793 794 /* Resolve references in extra attributes */ 795 private Object resolveRefs(Object o, OpenApi openApi, Deque<String> refStack, int maxDepth) { 796 if (o instanceof JsonMap om) { 797 var ref2 = om.get("$ref"); 798 if (ref2 instanceof CharSequence) { 799 var sref = ref2.toString(); 800 if (refStack.contains(sref) || refStack.size() >= maxDepth) 801 return o; 802 refStack.addLast(sref); 803 var o2 = openApi.findRef(sref, Object.class); 804 o2 = resolveRefs(o2, openApi, refStack, maxDepth); 805 refStack.removeLast(); 806 return o2; 807 } 808 for (var e : om.entrySet()) 809 e.setValue(resolveRefs(e.getValue(), openApi, refStack, maxDepth)); 810 } 811 if (o instanceof JsonList x) 812 for (var li = x.listIterator(); li.hasNext();) 813 li.set(resolveRefs(li.next(), openApi, refStack, maxDepth)); 814 return o; 815 } 816}