1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.bean.openapi3;
18
19 import static org.apache.juneau.commons.utils.AssertionUtils.*;
20 import static org.apache.juneau.commons.utils.CollectionUtils.*;
21 import static org.apache.juneau.commons.utils.StringUtils.*;
22 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
23 import static org.apache.juneau.commons.utils.Utils.*;
24 import static org.apache.juneau.internal.ConverterUtils.*;
25
26 import java.util.*;
27
28 import org.apache.juneau.annotation.*;
29 import org.apache.juneau.collections.*;
30 import org.apache.juneau.commons.collections.*;
31 import org.apache.juneau.json.*;
32
33 /**
34 * A limited subset of JSON-Schema's items object.
35 *
36 * <p>
37 * The Items Object is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are
38 * not located in "body" to describe the type of items in an array. This is particularly useful for query parameters,
39 * path parameters, and header parameters that accept arrays.
40 *
41 * <h5 class='section'>OpenAPI Specification:</h5>
42 * <p>
43 * The Items Object supports the following fields from JSON Schema:
44 * <ul class='spaced-list'>
45 * <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>
46 * <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>)
47 * <li><c>items</c> ({@link Items}) - Required if type is <js>"array"</js>. Describes the type of items in the array
48 * <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>
49 * <li><c>default</c> (any) - The default value
50 * <li><c>maximum</c> (number), <c>exclusiveMaximum</c> (boolean), <c>minimum</c> (number), <c>exclusiveMinimum</c> (boolean) - Numeric constraints
51 * <li><c>maxLength</c> (integer), <c>minLength</c> (integer), <c>pattern</c> (string) - String constraints
52 * <li><c>maxItems</c> (integer), <c>minItems</c> (integer), <c>uniqueItems</c> (boolean) - Array constraints
53 * <li><c>enum</c> (array) - Possible values for this item
54 * <li><c>multipleOf</c> (number) - Must be a multiple of this value
55 * </ul>
56 *
57 * <h5 class='section'>Example:</h5>
58 * <p class='bcode'>
59 * <jc>// Construct using SwaggerBuilder.</jc>
60 * Items <jv>x</jv> = <jsm>items</jsm>(<js>"string"</js>).minLength(2);
61 *
62 * <jc>// Serialize using JsonSerializer.</jc>
63 * String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
64 *
65 * <jc>// Or just use toString() which does the same as above.</jc>
66 * String <jv>json</jv> = <jv>x</jv>.toString();
67 * </p>
68 * <p class='bcode'>
69 * <jc>// Output</jc>
70 * {
71 * <js>"type"</js>: <js>"string"</js>,
72 * <js>"minLength"</js>: 2
73 * }
74 * </p>
75 *
76 * <h5 class='section'>See Also:</h5><ul>
77 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#items-object">OpenAPI Specification > Items Object</a>
78 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a>
79 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
80 * </ul>
81 */
82 public class Items extends OpenApiElement {
83
84 private static final String[] VALID_TYPES = { "string", "number", "integer", "boolean", "array" };
85 private static final String[] VALID_COLLECTION_FORMATS = { "csv", "ssv", "tsv", "pipes", "multi" };
86
87 private String type, format, collectionFormat, pattern, ref;
88 private Number maximum, minimum, multipleOf;
89 private Integer maxLength, minLength, maxItems, minItems;
90 private Boolean exclusiveMaximum, exclusiveMinimum, uniqueItems;
91 private Items items; // NOSONAR - Intentional naming.
92 private Object default_;
93 private List<Object> enum_ = list();
94
95 /**
96 * Default constructor.
97 */
98 public Items() {}
99
100 /**
101 * Copy constructor.
102 *
103 * @param copyFrom The object to copy.
104 */
105 public Items(Items copyFrom) {
106 super(copyFrom);
107
108 this.type = copyFrom.type;
109 this.format = copyFrom.format;
110 this.collectionFormat = copyFrom.collectionFormat;
111 this.pattern = copyFrom.pattern;
112 this.maximum = copyFrom.maximum;
113 this.minimum = copyFrom.minimum;
114 this.multipleOf = copyFrom.multipleOf;
115 this.maxLength = copyFrom.maxLength;
116 this.minLength = copyFrom.minLength;
117 this.maxItems = copyFrom.maxItems;
118 this.minItems = copyFrom.minItems;
119 this.exclusiveMaximum = copyFrom.exclusiveMaximum;
120 this.exclusiveMinimum = copyFrom.exclusiveMinimum;
121 this.uniqueItems = copyFrom.uniqueItems;
122 this.items = copyFrom.items == null ? null : copyFrom.items.copy();
123 this.default_ = copyFrom.default_;
124 if (nn(copyFrom.enum_))
125 this.enum_.addAll(copyOf(copyFrom.enum_));
126 this.ref = copyFrom.ref;
127 }
128
129 /**
130 * Adds one or more values to the <property>enum</property> property.
131 *
132 * @param values
133 * The values to add to this property.
134 * <br>Ignored if <jk>null</jk>.
135 * @return This object
136 */
137 public Items addEnum(Object...values) {
138 if (nn(values))
139 for (var v : values)
140 if (nn(v))
141 enum_.add(v);
142 return this;
143 }
144
145 /**
146 * Make a deep copy of this object.
147 *
148 * @return A deep copy of this object.
149 */
150 public Items copy() {
151 return new Items(this);
152 }
153
154 @Override /* Overridden from SwaggerElement */
155 public <T> T get(String property, Class<T> type) {
156 assertArgNotNull("property", property);
157 return switch (property) {
158 case "type" -> toType(getType(), type);
159 case "format" -> toType(getFormat(), type);
160 case "items" -> toType(getItems(), type);
161 case "collectionFormat" -> toType(getCollectionFormat(), type);
162 case "default" -> toType(getDefault(), type);
163 case "maximum" -> toType(getMaximum(), type);
164 case "exclusiveMaximum" -> toType(getExclusiveMaximum(), type);
165 case "minimum" -> toType(getMinimum(), type);
166 case "exclusiveMinimum" -> toType(getExclusiveMinimum(), type);
167 case "maxLength" -> toType(getMaxLength(), type);
168 case "minLength" -> toType(getMinLength(), type);
169 case "pattern" -> toType(getPattern(), type);
170 case "maxItems" -> toType(getMaxItems(), type);
171 case "minItems" -> toType(getMinItems(), type);
172 case "uniqueItems" -> toType(getUniqueItems(), type);
173 case "enum" -> toType(getEnum(), type);
174 case "multipleOf" -> toType(getMultipleOf(), type);
175 case "$ref" -> toType(getRef(), type);
176 default -> super.get(property, type);
177 };
178 }
179
180 /**
181 * Bean property getter: <property>collectionFormat</property>.
182 *
183 * <p>
184 * Determines the format of the array if type array is used.
185 *
186 * @return The property value, or <jk>null</jk> if it is not set.
187 */
188 public String getCollectionFormat() { return collectionFormat; }
189
190 /**
191 * Bean property getter: <property>default</property>.
192 *
193 * <p>
194 * Declares the value of the item that the server will use if none is provided.
195 *
196 * <h5 class='section'>Notes:</h5>
197 * <ul class='spaced-list'>
198 * <li>
199 * <js>"default"</js> has no meaning for required items.
200 * <li>
201 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for the data type.
202 * </ul>
203 *
204 * @return The property value, or <jk>null</jk> if it is not set.
205 */
206 public Object getDefault() { return default_; }
207
208 /**
209 * Bean property getter: <property>enum</property>.
210 *
211 * @return The property value, or <jk>null</jk> if it is not set.
212 */
213 public List<Object> getEnum() { return nullIfEmpty(enum_); }
214
215 /**
216 * Bean property getter: <property>exclusiveMaximum</property>.
217 *
218 * @return The property value, or <jk>null</jk> if it is not set.
219 */
220 public Boolean getExclusiveMaximum() { return exclusiveMaximum; }
221
222 /**
223 * Bean property getter: <property>exclusiveMinimum</property>.
224 *
225 * @return The property value, or <jk>null</jk> if it is not set.
226 */
227 public Boolean getExclusiveMinimum() { return exclusiveMinimum; }
228
229 /**
230 * Bean property getter: <property>format</property>.
231 *
232 * <p>
233 * The extending format for the previously mentioned <code>type</code>.
234 *
235 * @return The property value, or <jk>null</jk> if it is not set.
236 */
237 public String getFormat() { return format; }
238
239 /**
240 * Bean property getter: <property>items</property>.
241 *
242 * <p>
243 * Describes the type of items in the array.
244 *
245 * @return The property value, or <jk>null</jk> if it is not set.
246 */
247 public Items getItems() { return items; }
248
249 /**
250 * Bean property getter: <property>maximum</property>.
251 *
252 * @return The property value, or <jk>null</jk> if it is not set.
253 */
254 public Number getMaximum() { return maximum; }
255
256 /**
257 * Bean property getter: <property>maxItems</property>.
258 *
259 * @return The property value, or <jk>null</jk> if it is not set.
260 */
261 public Integer getMaxItems() { return maxItems; }
262
263 /**
264 * Bean property getter: <property>maxLength</property>.
265 *
266 * @return The property value, or <jk>null</jk> if it is not set.
267 */
268 public Integer getMaxLength() { return maxLength; }
269
270 /**
271 * Bean property getter: <property>minimum</property>.
272 *
273 * @return The property value, or <jk>null</jk> if it is not set.
274 */
275 public Number getMinimum() { return minimum; }
276
277 /**
278 * Bean property getter: <property>minItems</property>.
279 *
280 * @return The property value, or <jk>null</jk> if it is not set.
281 */
282 public Integer getMinItems() { return minItems; }
283
284 /**
285 * Bean property getter: <property>minLength</property>.
286 *
287 * @return The property value, or <jk>null</jk> if it is not set.
288 */
289 public Integer getMinLength() { return minLength; }
290
291 /**
292 * Bean property getter: <property>multipleOf</property>.
293 *
294 * @return The property value, or <jk>null</jk> if it is not set.
295 */
296 public Number getMultipleOf() { return multipleOf; }
297
298 /**
299 * Bean property getter: <property>pattern</property>.
300 *
301 * @return The property value, or <jk>null</jk> if it is not set.
302 */
303 public String getPattern() { return pattern; }
304
305 /**
306 * Bean property getter: <property>$ref</property>.
307 *
308 * @return The property value, or <jk>null</jk> if it is not set.
309 */
310 @Beanp("$ref")
311 public String getRef() { return ref; }
312
313 /**
314 * Bean property getter: <property>type</property>.
315 *
316 * <p>
317 * The internal type of the array.
318 *
319 * @return The property value, or <jk>null</jk> if it is not set.
320 */
321 public String getType() { return type; }
322
323 /**
324 * Bean property getter: <property>uniqueItems</property>.
325 *
326 * @return The property value, or <jk>null</jk> if it is not set.
327 */
328 public Boolean getUniqueItems() { return uniqueItems; }
329
330 @Override /* Overridden from SwaggerElement */
331 public Set<String> keySet() {
332 // @formatter:off
333 var s = setb(String.class)
334 .addIf(nn(ref), "$ref")
335 .addIf(nn(collectionFormat), "collectionFormat")
336 .addIf(nn(default_), "default")
337 .addIf(ne(enum_), "enum")
338 .addIf(nn(exclusiveMaximum), "exclusiveMaximum")
339 .addIf(nn(exclusiveMinimum), "exclusiveMinimum")
340 .addIf(nn(format), "format")
341 .addIf(nn(items), "items")
342 .addIf(nn(maxItems), "maxItems")
343 .addIf(nn(maxLength), "maxLength")
344 .addIf(nn(maximum), "maximum")
345 .addIf(nn(minItems), "minItems")
346 .addIf(nn(minLength), "minLength")
347 .addIf(nn(minimum), "minimum")
348 .addIf(nn(multipleOf), "multipleOf")
349 .addIf(nn(pattern), "pattern")
350 .addIf(nn(type), "type")
351 .addIf(nn(uniqueItems), "uniqueItems")
352 .build();
353 // @formatter:on
354 return new MultiSet<>(s, super.keySet());
355 }
356
357 /**
358 * Resolves any <js>"$ref"</js> attributes in this element.
359 *
360 * @param openApi The swagger document containing the definitions.
361 * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops.
362 * @param maxDepth
363 * The maximum depth to resolve references.
364 * <br>After that level is reached, <code>$ref</code> references will be left alone.
365 * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex.
366 * @return
367 * This object with references resolved.
368 * <br>May or may not be the same object.
369 */
370 public Items resolveRefs(OpenApi openApi, Deque<String> refStack, int maxDepth) {
371
372 if (nn(ref)) {
373 if (refStack.contains(ref) || refStack.size() >= maxDepth)
374 return this;
375 refStack.addLast(ref);
376 var r = openApi.findRef(ref, Items.class);
377 r = r.resolveRefs(openApi, refStack, maxDepth);
378 refStack.removeLast();
379 return r;
380 }
381
382 set("properties", resolveRefs(get("properties"), openApi, refStack, maxDepth));
383
384 if (nn(items))
385 items = items.resolveRefs(openApi, refStack, maxDepth);
386
387 set("example", null);
388
389 return this;
390 }
391
392 @Override /* Overridden from SwaggerElement */
393 public Items set(String property, Object value) {
394 assertArgNotNull("property", property);
395 return switch (property) {
396 case "$ref" -> setRef(value);
397 case "collectionFormat" -> setCollectionFormat(s(value));
398 case "default" -> setDefault(value);
399 case "enum" -> setEnum(value);
400 case "exclusiveMaximum" -> setExclusiveMaximum(toBoolean(value));
401 case "exclusiveMinimum" -> setExclusiveMinimum(toBoolean(value));
402 case "format" -> setFormat(s(value));
403 case "items" -> setItems(toType(value, Items.class));
404 case "maxItems" -> setMaxItems(toInteger(value));
405 case "maxLength" -> setMaxLength(toInteger(value));
406 case "maximum" -> setMaximum(toNumber(value));
407 case "minItems" -> setMinItems(toInteger(value));
408 case "minLength" -> setMinLength(toInteger(value));
409 case "minimum" -> setMinimum(toNumber(value));
410 case "multipleOf" -> setMultipleOf(toNumber(value));
411 case "pattern" -> setPattern(s(value));
412 case "type" -> setType(s(value));
413 case "uniqueItems" -> setUniqueItems(toBoolean(value));
414 default -> {
415 super.set(property, value);
416 yield this;
417 }
418 };
419 }
420
421 /**
422 * Bean property setter: <property>collectionFormat</property>.
423 *
424 * <p>
425 * Determines the format of the array if type array is used.
426 *
427 * @param value
428 * The new value for this property.
429 * <br>Valid values:
430 * <ul>
431 * <li><js>"csv"</js> (default) - comma separated values <code>foo,bar</code>.
432 * <li><js>"ssv"</js> - space separated values <code>foo bar</code>.
433 * <li><js>"tsv"</js> - tab separated values <code>foo\tbar</code>.
434 * <li><js>"pipes"</js> - pipe separated values <code>foo|bar</code>.
435 * </ul>
436 * <br>Can be <jk>null</jk> to unset the property.
437 * @return This object
438 */
439 public Items setCollectionFormat(String value) {
440 if (isStrict() && ! contains(value, VALID_COLLECTION_FORMATS))
441 throw rex("Invalid value passed in to setCollectionFormat(String). Value=''{0}'', valid values=[{1}]", value, toCdl(VALID_COLLECTION_FORMATS));
442 collectionFormat = value;
443 return this;
444 }
445
446 /**
447 * Bean property setter: <property>default</property>.
448 *
449 * <p>
450 * Declares the value of the item that the server will use if none is provided.
451 *
452 * <h5 class='section'>Notes:</h5>
453 * <ul class='spaced-list'>
454 * <li>
455 * <js>"default"</js> has no meaning for required items.
456 * <li>
457 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for the data type.
458 * </ul>
459 *
460 * @param value
461 * The new value for this property.
462 * <br>Can be <jk>null</jk> to unset the property.
463 * @return This object
464 */
465 public Items setDefault(Object value) {
466 default_ = value;
467 return this;
468 }
469
470 /**
471 * Bean property setter: <property>enum</property>.
472 *
473 * @param value
474 * The new value for this property.
475 * <br>Can be <jk>null</jk> to unset the property.
476 * @return This object
477 */
478 public Items setEnum(Collection<Object> value) {
479 enum_.clear();
480 if (nn(value))
481 enum_.addAll(value);
482 return this;
483 }
484
485 /**
486 * Adds one or more values to the <property>enum</property> property.
487 *
488 * @param values
489 * The values to add to this property.
490 * <br>Valid types:
491 * <ul>
492 * <li><code>Object</code>
493 * <li><code>Collection<Object></code>
494 * <li><code>String</code> - JSON array representation of <code>Collection<Object></code>
495 * <h5 class='figure'>Example:</h5>
496 * <p class='bcode'>
497 * enum_(<js>"['foo','bar']"</js>);
498 * </p>
499 * <li><code>String</code> - Individual values
500 * <h5 class='figure'>Example:</h5>
501 * <p class='bcode'>
502 * enum_(<js>"foo"</js>, <js>"bar"</js>);
503 * </p>
504 * </ul>
505 * <br>Ignored if <jk>null</jk>.
506 * @return This object
507 */
508 public Items setEnum(Object...values) { // NOSONAR - Intentional naming.
509 enum_ = listb(Object.class).sparse().addAny(enum_, values).build();
510 return this;
511 }
512
513 /**
514 * Bean property setter: <property>exclusiveMaximum</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 setExclusiveMaximum(Boolean value) {
522 exclusiveMaximum = value;
523 return this;
524 }
525
526 /**
527 * Bean property setter: <property>exclusiveMinimum</property>.
528 *
529 * @param value
530 * The new value for this property.
531 * <br>Can be <jk>null</jk> to unset the property.
532 * @return This object
533 */
534 public Items setExclusiveMinimum(Boolean value) {
535 exclusiveMinimum = value;
536 return this;
537 }
538
539 /**
540 * Bean property setter: <property>format</property>.
541 *
542 * <p>
543 * The extending format for the previously mentioned <code>type</code>.
544 *
545 * @param value
546 * The new value for this property.
547 * <br>Can be <jk>null</jk> to unset the property.
548 * @return This object
549 */
550 public Items setFormat(String value) {
551 format = value;
552 return this;
553 }
554
555 /**
556 * Bean property setter: <property>items</property>.
557 *
558 * <p>
559 * Describes the type of items in the array.
560 *
561 * @param value
562 * The new value for this property.
563 * <br>Property value is required if <code>type</code> is <js>"array"</js>.
564 * <br>Can be <jk>null</jk> to unset the property.
565 * @return This object
566 */
567 public Items setItems(Items value) {
568 items = value;
569 return this;
570 }
571
572 /**
573 * Bean property setter: <property>maximum</property>.
574 *
575 * @param value
576 * The new value for this property.
577 * <br>Can be <jk>null</jk> to unset the property.
578 * @return This object
579 */
580 public Items setMaximum(Number value) {
581 maximum = value;
582 return this;
583 }
584
585 /**
586 * Bean property setter: <property>maxItems</property>.
587 *
588 * @param value
589 * The new value for this property.
590 * <br>Can be <jk>null</jk> to unset the property.
591 * @return This object
592 */
593 public Items setMaxItems(Integer value) {
594 maxItems = value;
595 return this;
596 }
597
598 /**
599 * Bean property setter: <property>maxLength</property>.
600 *
601 * @param value
602 * The new value for this property.
603 * <br>Can be <jk>null</jk> to unset the property.
604 * @return This object
605 */
606 public Items setMaxLength(Integer value) {
607 maxLength = value;
608 return this;
609 }
610
611 /**
612 * Bean property setter: <property>minimum</property>.
613 *
614 * @param value
615 * The new value for this property.
616 * <br>Can be <jk>null</jk> to unset the property.
617 * @return This object
618 */
619 public Items setMinimum(Number value) {
620 minimum = value;
621 return this;
622 }
623
624 /**
625 * Bean property setter: <property>minItems</property>.
626 *
627 * @param value
628 * The new value for this property.
629 * <br>Can be <jk>null</jk> to unset the property.
630 * @return This object
631 */
632 public Items setMinItems(Integer value) {
633 minItems = value;
634 return this;
635 }
636
637 /**
638 * Bean property setter: <property>minLength</property>.
639 *
640 * @param value
641 * The new value for this property.
642 * <br>Can be <jk>null</jk> to unset the property.
643 * @return This object
644 */
645 public Items setMinLength(Integer value) {
646 minLength = value;
647 return this;
648 }
649
650 /**
651 * Bean property setter: <property>multipleOf</property>.
652 *
653 * @param value
654 * The new value for this property.
655 * <br>Can be <jk>null</jk> to unset the property.
656 * @return This object
657 */
658 public Items setMultipleOf(Number value) {
659 multipleOf = value;
660 return this;
661 }
662
663 /**
664 * Bean property setter: <property>pattern</property>.
665 *
666 * <p>
667 * This string SHOULD be a valid regular expression.
668 *
669 * @param value
670 * The new value for this property.
671 * <br>Can be <jk>null</jk> to unset the property.
672 * @return This object
673 */
674 public Items setPattern(String value) {
675 pattern = value;
676 return this;
677 }
678
679 /**
680 * Bean property setter: <property>$ref</property>.
681 *
682 * @param value
683 * The new value for this property.
684 * <br>Can be <jk>null</jk> to unset the property.
685 * @return This object
686 */
687 @Beanp("$ref")
688 public Items setRef(Object value) {
689 ref = s(value);
690 return this;
691 }
692
693 /**
694 * Bean property setter: <property>type</property>.
695 *
696 * <p>
697 * The internal type of the array.
698 *
699 * @param value
700 * The new value for this property.
701 * <br>Valid values:
702 * <ul>
703 * <li><js>"string"</js>
704 * <li><js>"number"</js>
705 * <li><js>"integer"</js>
706 * <li><js>"boolean"</js>
707 * <li><js>"array"</js>
708 * </ul>
709 * <br>Property value is required.
710 * <br>Can be <jk>null</jk> to unset the property.
711 * @return This object
712 */
713 public Items setType(String value) {
714 if (isStrict() && ! contains(value, VALID_TYPES))
715 throw illegalArg("Invalid value passed in to setType(String). Value=''{0}'', valid values={1}", value, Json5Serializer.DEFAULT.toString(VALID_TYPES));
716 type = value;
717 return this;
718 }
719
720 /**
721 * Bean property setter: <property>uniqueItems</property>.
722 *
723 * @param value
724 * The new value for this property.
725 * <br>Can be <jk>null</jk> to unset the property.
726 * @return This object
727 */
728 public Items setUniqueItems(Boolean value) {
729 uniqueItems = value;
730 return this;
731 }
732
733 @Override /* Overridden from OpenApiElement */
734 public Items strict(Object value) {
735 super.strict(value);
736 return this;
737 }
738
739 /* Resolve references in extra attributes */
740 private Object resolveRefs(Object o, OpenApi openApi, Deque<String> refStack, int maxDepth) {
741 if (o instanceof JsonMap om) {
742 var ref2 = om.get("$ref");
743 if (ref2 instanceof CharSequence) {
744 var sref = ref2.toString();
745 if (refStack.contains(sref) || refStack.size() >= maxDepth)
746 return o;
747 refStack.addLast(sref);
748 var o2 = openApi.findRef(sref, Object.class);
749 o2 = resolveRefs(o2, openApi, refStack, maxDepth);
750 refStack.removeLast();
751 return o2;
752 }
753 for (var e : om.entrySet())
754 e.setValue(resolveRefs(e.getValue(), openApi, refStack, maxDepth));
755 }
756 if (o instanceof JsonList o2)
757 for (var li = o2.listIterator(); li.hasNext();)
758 li.set(resolveRefs(li.next(), openApi, refStack, maxDepth));
759 return o;
760 }
761
762 @Override /* Overridden from SwaggerElement */
763 protected Items strict() {
764 super.strict();
765 return this;
766 }
767 }