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.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.StringUtils.*;
022import static org.apache.juneau.commons.utils.ThrowableUtils.*;
023import static org.apache.juneau.commons.utils.Utils.*;
024import static org.apache.juneau.internal.ConverterUtils.*;
025
026import java.util.*;
027
028import org.apache.juneau.annotation.*;
029import org.apache.juneau.collections.*;
030import org.apache.juneau.commons.collections.*;
031import org.apache.juneau.json.*;
032
033/**
034 * A limited subset of JSON-Schema's items object.
035 *
036 * <p>
037 * The Items Object is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are
038 * not located in "body" to describe the type of items in an array. This is particularly useful for query parameters,
039 * path parameters, and header parameters that accept arrays.
040 *
041 * <h5 class='section'>OpenAPI Specification:</h5>
042 * <p>
043 * The Items Object supports the following fields from JSON Schema:
044 * <ul class='spaced-list'>
045 *    <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>
046 *    <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>)
047 *    <li><c>items</c> ({@link Items}) - Required if type is <js>"array"</js>. Describes the type of items in the array
048 *    <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>
049 *    <li><c>default</c> (any) - The default value
050 *    <li><c>maximum</c> (number), <c>exclusiveMaximum</c> (boolean), <c>minimum</c> (number), <c>exclusiveMinimum</c> (boolean) - Numeric constraints
051 *    <li><c>maxLength</c> (integer), <c>minLength</c> (integer), <c>pattern</c> (string) - String constraints
052 *    <li><c>maxItems</c> (integer), <c>minItems</c> (integer), <c>uniqueItems</c> (boolean) - Array constraints
053 *    <li><c>enum</c> (array) - Possible values for this item
054 *    <li><c>multipleOf</c> (number) - Must be a multiple of this value
055 * </ul>
056 *
057 * <h5 class='section'>Example:</h5>
058 * <p class='bcode'>
059 *    <jc>// Construct using SwaggerBuilder.</jc>
060 *    Items <jv>x</jv> = <jsm>items</jsm>(<js>"string"</js>).minLength(2);
061 *
062 *    <jc>// Serialize using JsonSerializer.</jc>
063 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
064 *
065 *    <jc>// Or just use toString() which does the same as above.</jc>
066 *    String <jv>json</jv> = <jv>x</jv>.toString();
067 * </p>
068 * <p class='bcode'>
069 *    <jc>// Output</jc>
070 *    {
071 *       <js>"type"</js>: <js>"string"</js>,
072 *       <js>"minLength"</js>: 2
073 *    }
074 * </p>
075 *
076 * <h5 class='section'>See Also:</h5><ul>
077 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#items-object">OpenAPI Specification &gt; Items Object</a>
078 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a>
079 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
080 * </ul>
081 */
082public class Items extends OpenApiElement {
083
084   private static final String[] VALID_TYPES = { "string", "number", "integer", "boolean", "array" };
085   private static final String[] VALID_COLLECTION_FORMATS = { "csv", "ssv", "tsv", "pipes", "multi" };
086
087   private String type, format, collectionFormat, pattern, ref;
088   private Number maximum, minimum, multipleOf;
089   private Integer maxLength, minLength, maxItems, minItems;
090   private Boolean exclusiveMaximum, exclusiveMinimum, uniqueItems;
091   private Items items;  // NOSONAR - Intentional naming.
092   private Object default_;
093   private List<Object> enum_ = list();
094
095   /**
096    * Default constructor.
097    */
098   public Items() {}
099
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&lt;Object&gt;</code>
494    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;Object&gt;</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}