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