View Javadoc
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 &gt; 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&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 }