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.Utils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.util.*;
025
026import org.apache.juneau.annotation.*;
027import org.apache.juneau.commons.collections.*;
028
029/**
030 * Allows the definition of input and output data types.
031 *
032 * <p>
033 * The Schema Object allows the definition of input and output data types, including objects, primitives, and arrays.
034 * This object is an extended subset of the JSON Schema Specification Draft 4, with additional extensions provided
035 * by the OpenAPI Specification to allow for more complete documentation.
036 *
037 * <h5 class='section'>OpenAPI Specification:</h5>
038 * <p>
039 * The Schema Object supports all properties from JSON Schema Draft 4, including but not limited to:
040 * <ul class='spaced-list'>
041 *    <li><c>type</c> (string) - The data type. Values: <js>"string"</js>, <js>"number"</js>, <js>"integer"</js>, <js>"boolean"</js>, <js>"array"</js>, <js>"object"</js>
042 *    <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>)
043 *    <li><c>title</c> (string) - A short title for the schema
044 *    <li><c>description</c> (string) - A description of the schema (CommonMark syntax may be used)
045 *    <li><c>default</c> (any) - The default value
046 *    <li><c>multipleOf</c> (number) - Must be a multiple of this value
047 *    <li><c>maximum</c> (number) - Maximum value (inclusive by default)
048 *    <li><c>exclusiveMaximum</c> (boolean) - If true, maximum is exclusive
049 *    <li><c>minimum</c> (number) - Minimum value (inclusive by default)
050 *    <li><c>exclusiveMinimum</c> (boolean) - If true, minimum is exclusive
051 *    <li><c>maxLength</c> (integer) - Maximum string length
052 *    <li><c>minLength</c> (integer) - Minimum string length
053 *    <li><c>pattern</c> (string) - Regular expression pattern the string must match
054 *    <li><c>maxItems</c> (integer) - Maximum array length
055 *    <li><c>minItems</c> (integer) - Minimum array length
056 *    <li><c>uniqueItems</c> (boolean) - If true, array items must be unique
057 *    <li><c>maxProperties</c> (integer) - Maximum number of object properties
058 *    <li><c>minProperties</c> (integer) - Minimum number of object properties
059 *    <li><c>required</c> (array of string) - Required property names
060 *    <li><c>enum</c> (array) - Possible values for this schema
061 *    <li><c>properties</c> (map of {@link SchemaInfo}) - Object property definitions
062 *    <li><c>items</c> ({@link Items}) - Schema for array items
063 *    <li><c>allOf</c> (array of {@link SchemaInfo}) - Must validate against all schemas
064 *    <li><c>oneOf</c> (array of {@link SchemaInfo}) - Must validate against exactly one schema
065 *    <li><c>anyOf</c> (array of {@link SchemaInfo}) - Must validate against any schema
066 *    <li><c>not</c> ({@link SchemaInfo}) - Must not validate against this schema
067 *    <li><c>nullable</c> (boolean) - Allows the value to be null (OpenAPI 3.0 extension)
068 *    <li><c>discriminator</c> ({@link Discriminator}) - Discriminator for polymorphism (OpenAPI extension)
069 *    <li><c>readOnly</c> (boolean) - Relevant only for Schema properties (OpenAPI extension)
070 *    <li><c>writeOnly</c> (boolean) - Relevant only for Schema properties (OpenAPI extension)
071 *    <li><c>xml</c> ({@link Xml}) - XML representation details (OpenAPI extension)
072 *    <li><c>externalDocs</c> ({@link ExternalDocumentation}) - Additional external documentation (OpenAPI extension)
073 *    <li><c>example</c> (any) - Example value (OpenAPI extension)
074 *    <li><c>deprecated</c> (boolean) - Specifies that the schema is deprecated (OpenAPI extension)
075 * </ul>
076 *
077 * <h5 class='section'>Example:</h5>
078 * <p class='bjava'>
079 *    <jc>// Create a schema for a Pet object</jc>
080 *    SchemaInfo <jv>schema</jv> = <jk>new</jk> SchemaInfo()
081 *       .setType(<js>"object"</js>)
082 *       .setRequired(<js>"id"</js>, <js>"name"</js>)
083 *       .setProperties(
084 *          JsonMap.<jsm>of</jsm>(
085 *             <js>"id"</js>, <jk>new</jk> SchemaInfo().setType(<js>"integer"</js>).setFormat(<js>"int64"</js>),
086 *             <js>"name"</js>, <jk>new</jk> SchemaInfo().setType(<js>"string"</js>),
087 *             <js>"tag"</js>, <jk>new</jk> SchemaInfo().setType(<js>"string"</js>)
088 *          )
089 *       );
090 * </p>
091 *
092 * <h5 class='section'>See Also:</h5><ul>
093 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#schema-object">OpenAPI Specification &gt; Schema Object</a>
094 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/data-models/">OpenAPI Data Models</a>
095 *    <li class='link'><a class="doclink" href="https://json-schema.org/">JSON Schema Specification</a>
096 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
097 * </ul>
098 */
099public class SchemaInfo extends OpenApiElement {
100
101   private String format, title, description, pattern, ref, type;
102   private Number multipleOf, maximum, minimum;
103   private Integer maxLength, minLength, maxItems, minItems, maxProperties, minProperties;
104   private Boolean exclusiveMaximum, exclusiveMinimum, uniqueItems, nullable, writeOnly, readOnly, deprecated;
105   private Object default_,
106      example;
107   private Items items;
108   private Xml xml;
109   private ExternalDocumentation externalDocs;
110   private List<Object> allOf = list(), oneOf = list(), anyOf = list(), enum_ = list();
111   private List<String> required = list();
112   private Discriminator discriminator;
113   private Map<String,SchemaInfo> properties;
114   private SchemaInfo additionalProperties;
115   private SchemaInfo not;
116
117   /**
118    * Default constructor.
119    */
120   public SchemaInfo() {}
121
122   /**
123    * Copy constructor.
124    *
125    * @param copyFrom The object to copy.
126    */
127   public SchemaInfo(SchemaInfo copyFrom) {
128      super(copyFrom);
129
130      this.format = copyFrom.format;
131      this.title = copyFrom.title;
132      this.description = copyFrom.description;
133      this.ref = copyFrom.ref;
134      this.nullable = copyFrom.nullable;
135      this.writeOnly = copyFrom.writeOnly;
136      this.deprecated = copyFrom.deprecated;
137      this.pattern = copyFrom.pattern;
138      this.type = copyFrom.type;
139      this.discriminator = copyFrom.discriminator;
140      this.multipleOf = copyFrom.multipleOf;
141      this.maximum = copyFrom.maximum;
142      this.minimum = copyFrom.minimum;
143      this.maxLength = copyFrom.maxLength;
144      this.minLength = copyFrom.minLength;
145      this.maxItems = copyFrom.maxItems;
146      this.minItems = copyFrom.minItems;
147      this.maxProperties = copyFrom.maxProperties;
148      this.minProperties = copyFrom.minProperties;
149      this.exclusiveMaximum = copyFrom.exclusiveMaximum;
150      this.exclusiveMinimum = copyFrom.exclusiveMinimum;
151      this.uniqueItems = copyFrom.uniqueItems;
152      this.readOnly = copyFrom.readOnly;
153      this.default_ = copyFrom.default_;
154      this.example = copyFrom.example;
155      this.items = copyFrom.items == null ? null : copyFrom.items.copy();
156      this.xml = copyFrom.xml == null ? null : copyFrom.xml.copy();
157      this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy();
158      if (nn(copyFrom.enum_))
159         enum_.addAll(copyFrom.enum_);
160      if (nn(copyFrom.allOf))
161         allOf.addAll(copyFrom.allOf);
162      if (nn(copyFrom.required))
163         required.addAll(copyFrom.required);
164      if (nn(copyFrom.anyOf))
165         anyOf.addAll(copyFrom.anyOf);
166      if (nn(copyFrom.oneOf))
167         oneOf.addAll(copyFrom.oneOf);
168      this.properties = copyOf(copyFrom.properties, SchemaInfo::copy);
169      this.additionalProperties = copyFrom.additionalProperties == null ? null : copyFrom.additionalProperties.copy();
170      this.not = copyFrom.not == null ? null : copyFrom.not.copy();
171   }
172
173   /**
174    * Adds one or more values to the <property>allOf</property> property.
175    *
176    * @param values
177    *    The values to add to this property.
178    *    <br>Valid types:
179    *    <ul>
180    *       <li><code>Object</code>
181    *       <li><code>Collection&lt;Object&gt;</code>
182    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;Object&gt;</code>
183    *          <h5 class='figure'>Example:</h5>
184    *          <p class='bcode'>
185    *    allOf(<js>"['foo','bar']"</js>);
186    *          </p>
187    *       <li><code>String</code> - Individual values
188    *          <h5 class='figure'>Example:</h5>
189    *          <p class='bcode'>
190    *    allOf(<js>"foo"</js>, <js>"bar"</js>);
191    *          </p>
192    *    </ul>
193    *    <br>Ignored if <jk>null</jk>.
194    * @return This object
195    */
196   public SchemaInfo addAllOf(Object...values) {
197      if (nn(values))
198         for (var v : values)
199            if (nn(v))
200               allOf.add(v);
201      return this;
202   }
203
204   /**
205    * Adds one or more values to the <property>allOf</property> property.
206    *
207    * @param values
208    *    The values to add to this property.
209    *    <br>Valid types:
210    *    <ul>
211    *       <li><code>Object</code>
212    *       <li><code>Collection&lt;Object&gt;</code>
213    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;Object&gt;</code>
214    *          <h5 class='figure'>Example:</h5>
215    *          <p class='bcode'>
216    *    allOf(<js>"['foo','bar']"</js>);
217    *          </p>
218    *       <li><code>String</code> - Individual values
219    *          <h5 class='figure'>Example:</h5>
220    *          <p class='bcode'>
221    *    allOf(<js>"foo"</js>, <js>"bar"</js>);
222    *          </p>
223    *    </ul>
224    *    <br>Ignored if <jk>null</jk>.
225    * @return This object
226    */
227   public SchemaInfo addAnyOf(Object...values) {
228      if (nn(values))
229         for (var v : values)
230            if (nn(v))
231               anyOf.add(v);
232      return this;
233   }
234
235   /**
236    * Adds one or more values to the <property>enum</property> property.
237    *
238    * @param values
239    *    The values to add to this property.
240    *    <br>Valid types:
241    *    <ul>
242    *       <li><code>Object</code>
243    *       <li><code>Collection&lt;Object&gt;</code>
244    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;Object&gt;</code>
245    *          <h5 class='figure'>Example:</h5>
246    *          <p class='bcode'>
247    *    enum_(<js>"['foo','bar']"</js>);
248    *          </p>
249    *       <li><code>String</code> - Individual values
250    *          <h5 class='figure'>Example:</h5>
251    *          <p class='bcode'>
252    *    enum_(<js>"foo"</js>, <js>"bar"</js>);
253    *          </p>
254    *    </ul>
255    *    <br>Ignored if <jk>null</jk>.
256    * @return This object
257    */
258   public SchemaInfo addEnum(Object...values) {
259      if (nn(values))
260         for (var v : values)
261            if (nn(v))
262               enum_.add(v);
263      return this;
264   }
265
266   /**
267    * Adds one or more values to the <property>allOf</property> property.
268    *
269    * @param values
270    *    The values to add to this property.
271    *    <br>Valid types:
272    *    <ul>
273    *       <li><code>Object</code>
274    *       <li><code>Collection&lt;Object&gt;</code>
275    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;Object&gt;</code>
276    *          <h5 class='figure'>Example:</h5>
277    *          <p class='bcode'>
278    *    allOf(<js>"['foo','bar']"</js>);
279    *          </p>
280    *       <li><code>String</code> - Individual values
281    *          <h5 class='figure'>Example:</h5>
282    *          <p class='bcode'>
283    *    allOf(<js>"foo"</js>, <js>"bar"</js>);
284    *          </p>
285    *    </ul>
286    *    <br>Ignored if <jk>null</jk>.
287    * @return This object
288    */
289   public SchemaInfo addOneOf(Object...values) {
290      if (nn(values))
291         for (var v : values)
292            if (nn(v))
293               oneOf.add(v);
294      return this;
295   }
296
297   /**
298    * Same as {@link #addRequired(String...)}.
299    *
300    * @param values
301    *    The new value for this property.
302    *    <br>Valid types:
303    *    <ul>
304    *       <li><code>Collection&lt;String&gt;</code>
305    *       <li><code>String</code> - JSON array representation of <code>Collection&lt;String&gt;</code>
306    *          <h5 class='figure'>Example:</h5>
307    *          <p class='bcode'>
308    *    schemes(<js>"['scheme1','scheme2']"</js>);
309    *          </p>
310    *       <li><code>String</code> - Individual values
311    *          <h5 class='figure'>Example:</h5>
312    *          <p class='bcode'>
313    *    schemes(<js>"scheme1</js>, <js>"scheme2"</js>);
314    *          </p>
315    *    </ul>
316    * @return This object
317    */
318   public SchemaInfo addRequired(String...values) {
319      if (nn(values))
320         for (var v : values)
321            if (nn(v))
322               required.add(v);
323      return this;
324   }
325
326   /**
327    * Make a deep copy of this object.
328    *
329    * @return A deep copy of this object.
330    */
331   public SchemaInfo copy() {
332      return new SchemaInfo(this);
333   }
334
335   @Override /* Overridden from SwaggerElement */
336   public <T> T get(String property, Class<T> type) {
337      assertArgNotNull("property", property);
338      return switch (property) {  // NOSONAR
339         case "format" -> toType(getFormat(), type);
340         case "title" -> toType(getTitle(), type);
341         case "description" -> toType(getDescription(), type);
342         case "default" -> toType(getDefault(), type);
343         case "multipleOf" -> toType(getMultipleOf(), type);
344         case "maximum" -> toType(getMaximum(), type);
345         case "exclusiveMaximum" -> toType(getExclusiveMaximum(), type);
346         case "minimum" -> toType(getMinimum(), type);
347         case "exclusiveMinimum" -> toType(getExclusiveMinimum(), type);
348         case "maxLength" -> toType(getMaxLength(), type);
349         case "minLength" -> toType(getMinLength(), type);
350         case "pattern" -> toType(getPattern(), type);
351         case "maxItems" -> toType(getMaxItems(), type);
352         case "minItems" -> toType(getMinItems(), type);
353         case "uniqueItems" -> toType(getUniqueItems(), type);
354         case "maxProperties" -> toType(getMaxProperties(), type);
355         case "minProperties" -> toType(getMinProperties(), type);
356         case "required" -> toType(getRequired(), type);
357         case "enum" -> toType(getEnum(), type);
358         case "type" -> toType(getType(), type);
359         case "items" -> toType(getItems(), type);
360         case "allOf" -> toType(getAllOf(), type);
361         case "oneOf" -> toType(getOneOf(), type);
362         case "anyOf" -> toType(getAnyOf(), type);
363         case "properties" -> toType(getProperties(), type);
364         case "additionalProperties" -> toType(getAdditionalProperties(), type);
365         case "not" -> toType(getNot(), type);
366         case "nullable" -> toType(getNullable(), type);
367         case "deprecated" -> toType(getDeprecated(), type);
368         case "discriminator" -> toType(getDiscriminator(), type);
369         case "readOnly" -> toType(getReadOnly(), type);
370         case "writeOnly" -> toType(getWriteOnly(), type);
371         case "xml" -> toType(getXml(), type);
372         case "externalDocs" -> toType(getExternalDocs(), type);
373         case "example" -> toType(getExample(), type);
374         case "$ref" -> toType(getRef(), type);
375         default -> super.get(property, type);
376      };
377   }
378
379   /**
380    * Bean property getter:  <property>additionalProperties</property>.
381    *
382    * @return The property value, or <jk>null</jk> if it is not set.
383    */
384   public SchemaInfo getAdditionalProperties() { return additionalProperties; }
385
386   /**
387    * Bean property getter:  <property>allOf</property>.
388    *
389    * @return The property value, or <jk>null</jk> if it is not set.
390    */
391   public List<Object> getAllOf() { return nullIfEmpty(allOf); }
392
393   /**
394    * Bean property getter:  <property>allOf</property>.
395    *
396    * @return The property value, or <jk>null</jk> if it is not set.
397    */
398   public List<Object> getAnyOf() { return nullIfEmpty(anyOf); }
399
400   /**
401    * Bean property getter:  <property>default</property>.
402    *
403    * <p>
404    * Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object.
405    *
406    * @return The property value, or <jk>null</jk> if it is not set.
407    */
408   public Object getDefault() { return default_; }
409
410   /**
411    * Bean property getter:  <property>deprecated</property>.
412    *
413    * @return The property value, or <jk>null</jk> if it is not set.
414    */
415   public Boolean getDeprecated() { return deprecated; }
416
417   /**
418    * Bean property getter:  <property>description</property>.
419    *
420    * @return The property value, or <jk>null</jk> if it is not set.
421    */
422   public String getDescription() { return description; }
423
424   /**
425    * Bean property getter:  <property>discriminator</property>.
426    *
427    * @return The property value, or <jk>null</jk> if it is not set.
428    */
429   public Discriminator getDiscriminator() { return discriminator; }
430
431   /**
432    * Bean property getter:  <property>enum</property>.
433    *
434    * @return The property value, or <jk>null</jk> if it is not set.
435    */
436   public List<Object> getEnum() { return nullIfEmpty(enum_); }
437
438   /**
439    * Bean property getter:  <property>example</property>.
440    *
441    * @return The property value, or <jk>null</jk> if it is not set.
442    */
443   public Object getExample() { return example; }
444
445   /**
446    * Bean property getter:  <property>exclusiveMaximum</property>.
447    *
448    * @return The property value, or <jk>null</jk> if it is not set.
449    */
450   public Boolean getExclusiveMaximum() { return exclusiveMaximum; }
451
452   /**
453    * Bean property getter:  <property>exclusiveMinimum</property>.
454    *
455    * @return The property value, or <jk>null</jk> if it is not set.
456    */
457   public Boolean getExclusiveMinimum() { return exclusiveMinimum; }
458
459   /**
460    * Bean property getter:  <property>externalDocs</property>.
461    *
462    * @return The property value, or <jk>null</jk> if it is not set.
463    */
464   public ExternalDocumentation getExternalDocs() { return externalDocs; }
465
466   /**
467    * Bean property getter:  <property>format</property>.
468    *
469    * @return The property value, or <jk>null</jk> if it is not set.
470    */
471   public String getFormat() { return format; }
472
473   /**
474    * Bean property getter:  <property>items</property>.
475    *
476    * @return The property value, or <jk>null</jk> if it is not set.
477    */
478   public Items getItems() { return items; }
479
480   /**
481    * Bean property getter:  <property>maximum</property>.
482    *
483    * @return The property value, or <jk>null</jk> if it is not set.
484    */
485   public Number getMaximum() { return maximum; }
486
487   /**
488    * Bean property getter:  <property>maxItems</property>.
489    *
490    * @return The property value, or <jk>null</jk> if it is not set.
491    */
492   public Integer getMaxItems() { return maxItems; }
493
494   /**
495    * Bean property getter:  <property>maxLength</property>.
496    *
497    * @return The property value, or <jk>null</jk> if it is not set.
498    */
499   public Integer getMaxLength() { return maxLength; }
500
501   /**
502    * Bean property getter:  <property>maxProperties</property>.
503    *
504    * @return The property value, or <jk>null</jk> if it is not set.
505    */
506   public Integer getMaxProperties() { return maxProperties; }
507
508   /**
509    * Bean property getter:  <property>minimum</property>.
510    *
511    * @return The property value, or <jk>null</jk> if it is not set.
512    */
513   public Number getMinimum() { return minimum; }
514
515   /**
516    * Bean property getter:  <property>minItems</property>.
517    *
518    * @return The property value, or <jk>null</jk> if it is not set.
519    */
520   public Integer getMinItems() { return minItems; }
521
522   /**
523    * Bean property getter:  <property>minLength</property>.
524    *
525    * @return The property value, or <jk>null</jk> if it is not set.
526    */
527   public Integer getMinLength() { return minLength; }
528
529   /**
530    * Bean property getter:  <property>minProperties</property>.
531    *
532    * @return The property value, or <jk>null</jk> if it is not set.
533    */
534   public Integer getMinProperties() { return minProperties; }
535
536   /**
537    * Bean property getter:  <property>multipleOf</property>.
538    *
539    * @return The property value, or <jk>null</jk> if it is not set.
540    */
541   public Number getMultipleOf() { return multipleOf; }
542
543   /**
544    * Bean property getter:  <property>not</property>.
545    *
546    * @return The property value, or <jk>null</jk> if it is not set.
547    */
548   public SchemaInfo getNot() { return not; }
549
550   /**
551    * Bean property getter:  <property>uniqueItems</property>.
552    *
553    * @return The property value, or <jk>null</jk> if it is not set.
554    */
555   public Boolean getNullable() { return nullable; }
556
557   /**
558    * Bean property getter:  <property>allOf</property>.
559    *
560    * @return The property value, or <jk>null</jk> if it is not set.
561    */
562   public List<Object> getOneOf() { return nullIfEmpty(oneOf); }
563
564   /**
565    * Bean property getter:  <property>pattern</property>.
566    *
567    * @return The property value, or <jk>null</jk> if it is not set.
568    */
569   public String getPattern() { return pattern; }
570
571   /**
572    * Bean property getter:  <property>properties</property>.
573    *
574    * @return The property value, or <jk>null</jk> if it is not set.
575    */
576   public Map<String,SchemaInfo> getProperties() { return properties; }
577
578   /**
579    * Bean property getter:  <property>readOnly</property>.
580    *
581    * @return The property value, or <jk>null</jk> if it is not set.
582    */
583   public Boolean getReadOnly() { return readOnly; }
584
585   /**
586    * Bean property getter:  <property>$ref</property>.
587    *
588    * @return The property value, or <jk>null</jk> if it is not set.
589    */
590   @Beanp("$ref")
591   public String getRef() { return ref; }
592
593   /**
594    * Bean property getter:  <property>required</property>.
595    *
596    * <p>
597    * The list of required properties.
598    *
599    * @return The property value, or <jk>null</jk> if it is not set.
600    */
601   public List<String> getRequired() { return nullIfEmpty(required); }
602
603   /**
604    * Bean property getter:  <property>title</property>.
605    *
606    * @return The property value, or <jk>null</jk> if it is not set.
607    */
608   public String getTitle() { return title; }
609
610   /**
611    * Bean property getter:  <property>type</property>.
612    *
613    * @return The property value, or <jk>null</jk> if it is not set.
614    */
615   public String getType() { return type; }
616
617   /**
618    * Bean property getter:  <property>uniqueItems</property>.
619    *
620    * @return The property value, or <jk>null</jk> if it is not set.
621    */
622   public Boolean getUniqueItems() { return uniqueItems; }
623
624   /**
625    * Bean property getter:  <property>WriteOnly</property>.
626    *
627    * @return The property value, or <jk>null</jk> if it is not set.
628    */
629   public Boolean getWriteOnly() { return writeOnly; }
630
631   /**
632    * Bean property getter:  <property>xml</property>.
633    *
634    * @return The property value, or <jk>null</jk> if it is not set.
635    */
636   public Xml getXml() { return xml; }
637
638   @Override /* Overridden from SwaggerElement */
639   public Set<String> keySet() {
640      // @formatter:off
641      var s = setb(String.class)
642         .addIf(nn(ref), "$ref")
643         .addIf(nn(additionalProperties), "additionalProperties")
644         .addIf(ne(allOf), "allOf")
645         .addIf(ne(anyOf), "anyOf")
646         .addIf(nn(default_), "default")
647         .addIf(nn(deprecated), "deprecated")
648         .addIf(nn(description), "description")
649         .addIf(nn(discriminator), "discriminator")
650         .addIf(ne(enum_), "enum")
651         .addIf(nn(example), "example")
652         .addIf(nn(exclusiveMaximum), "exclusiveMaximum")
653         .addIf(nn(exclusiveMinimum), "exclusiveMinimum")
654         .addIf(nn(externalDocs), "externalDocs")
655         .addIf(nn(format), "format")
656         .addIf(nn(items), "items")
657         .addIf(nn(maxItems), "maxItems")
658         .addIf(nn(maxLength), "maxLength")
659         .addIf(nn(maxProperties), "maxProperties")
660         .addIf(nn(maximum), "maximum")
661         .addIf(nn(minItems), "minItems")
662         .addIf(nn(minLength), "minLength")
663         .addIf(nn(minProperties), "minProperties")
664         .addIf(nn(minimum), "minimum")
665         .addIf(nn(multipleOf), "multipleOf")
666         .addIf(nn(not), "not")
667         .addIf(nn(nullable), "nullable")
668         .addIf(ne(oneOf), "oneOf")
669         .addIf(nn(pattern), "pattern")
670         .addIf(nn(properties), "properties")
671         .addIf(nn(readOnly), "readOnly")
672         .addIf(ne(required), "required")
673         .addIf(nn(title), "title")
674         .addIf(nn(type), "type")
675         .addIf(nn(uniqueItems), "uniqueItems")
676         .addIf(nn(writeOnly), "writeOnly")
677         .addIf(nn(xml), "xml")
678         .build();
679      // @formatter:on
680      return new MultiSet<>(s, super.keySet());
681   }
682
683   /**
684    * Resolves any <js>"$ref"</js> attributes in this element.
685    *
686    * @param openApi The swagger document containing the definitions.
687    * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops.
688    * @param maxDepth
689    *    The maximum depth to resolve references.
690    *    <br>After that level is reached, <code>$ref</code> references will be left alone.
691    *    <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex.
692    * @return
693    *    This object with references resolved.
694    *    <br>May or may not be the same object.
695    */
696   public SchemaInfo resolveRefs(OpenApi openApi, Deque<String> refStack, int maxDepth) {
697
698      if (nn(ref)) {
699         if (refStack.contains(ref) || refStack.size() >= maxDepth)
700            return this;
701         refStack.addLast(ref);
702         var r = openApi.findRef(ref, SchemaInfo.class);
703         r = r.resolveRefs(openApi, refStack, maxDepth);
704         refStack.removeLast();
705         return r;
706      }
707
708      if (nn(items))
709         items = items.resolveRefs(openApi, refStack, maxDepth);
710
711      if (nn(properties))
712         for (var e : properties.entrySet())
713            e.setValue(e.getValue().resolveRefs(openApi, refStack, maxDepth));
714
715      if (nn(additionalProperties))
716         additionalProperties = additionalProperties.resolveRefs(openApi, refStack, maxDepth);
717
718      this.example = null;
719
720      return this;
721   }
722
723   @Override /* Overridden from SwaggerElement */
724   public SchemaInfo set(String property, Object value) {
725      assertArgNotNull("property", property);
726      return switch (property) {  // NOSONAR
727         case "$ref" -> setRef(value);
728         case "additionalProperties" -> setAdditionalProperties(toType(value, SchemaInfo.class));
729         case "allOf" -> setAllOf(listb(Object.class).addAny(value).sparse().build());
730         case "anyOf" -> setAnyOf(listb(Object.class).addAny(value).sparse().build());
731         case "default" -> setDefault(value);
732         case "deprecated" -> setDeprecated(toBoolean(value));
733         case "description" -> setDescription(s(value));
734         case "discriminator" -> setDiscriminator(toType(value, Discriminator.class));
735         case "enum" -> setEnum(listb(Object.class).addAny(value).sparse().build());
736         case "example" -> setExample(value);
737         case "exclusiveMaximum" -> setExclusiveMaximum(toBoolean(value));
738         case "exclusiveMinimum" -> setExclusiveMinimum(toBoolean(value));
739         case "externalDocs" -> setExternalDocs(toType(value, ExternalDocumentation.class));
740         case "format" -> setFormat(s(value));
741         case "items" -> setItems(toType(value, Items.class));
742         case "maxItems" -> setMaxItems(toInteger(value));
743         case "maxLength" -> setMaxLength(toInteger(value));
744         case "maxProperties" -> setMaxProperties(toInteger(value));
745         case "maximum" -> setMaximum(toNumber(value));
746         case "minItems" -> setMinItems(toInteger(value));
747         case "minLength" -> setMinLength(toInteger(value));
748         case "minProperties" -> setMinProperties(toInteger(value));
749         case "minimum" -> setMinimum(toNumber(value));
750         case "multipleOf" -> setMultipleOf(toNumber(value));
751         case "not" -> setNot(toType(value, SchemaInfo.class));
752         case "nullable" -> setNullable(toBoolean(value));
753         case "oneOf" -> setOneOf(listb(Object.class).addAny(value).sparse().build());
754         case "pattern" -> setPattern(s(value));
755         case "properties" -> setProperties(toMapBuilder(value, String.class, SchemaInfo.class).sparse().build());
756         case "readOnly" -> setReadOnly(toBoolean(value));
757         case "required" -> setRequired(listb(String.class).addAny(value).sparse().build());
758         case "title" -> setTitle(s(value));
759         case "type" -> setType(s(value));
760         case "uniqueItems" -> setUniqueItems(toBoolean(value));
761         case "writeOnly" -> setWriteOnly(toBoolean(value));
762         case "xml" -> setXml(toType(value, Xml.class));
763         default -> {
764            super.set(property, value);
765            yield this;
766         }
767      };
768   }
769
770   /**
771    * Bean property setter:  <property>additionalProperties</property>.
772    *
773    * @param value
774    *    The new value for this property.
775    *    <br>Can be <jk>null</jk> to unset the property.
776    * @return This object
777    */
778   public SchemaInfo setAdditionalProperties(SchemaInfo value) {
779      additionalProperties = value;
780      return this;
781   }
782
783   /**
784    * Bean property setter:  <property>allOf</property>.
785    *
786    * @param value
787    *    The new value for this property.
788    *    <br>Can be <jk>null</jk> to unset the property.
789    * @return This object
790    */
791   public SchemaInfo setAllOf(Collection<Object> value) {
792      allOf.clear();
793      if (nn(value))
794         allOf.addAll(value);
795      return this;
796   }
797
798   /**
799    * Bean property setter:  <property>allOf</property>.
800    *
801    * @param value
802    *    The new value for this property.
803    *    <br>Can be <jk>null</jk> to unset the property.
804    * @return This object
805    */
806   public SchemaInfo setAnyOf(Collection<Object> value) {
807      anyOf.clear();
808      if (nn(value))
809         anyOf.addAll(value);
810      return this;
811   }
812
813   /**
814    * Bean property setter:  <property>default</property>.
815    *
816    * <p>
817    * Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object.
818    *
819    * @param value
820    *    The new value for this property.
821    *    <br>Can be <jk>null</jk> to unset the property.
822    * @return This object
823    */
824   public SchemaInfo setDefault(Object value) {
825      default_ = value;
826      return this;
827   }
828
829   /**
830    * Bean property setter:  <property>deprecated</property>.
831    *
832    * @param value
833    *    The new value for this property.
834    *    <br>Can be <jk>null</jk> to unset the property.
835    * @return This object
836    */
837   public SchemaInfo setDeprecated(Boolean value) {
838      deprecated = value;
839      return this;
840   }
841
842   /**
843    * Bean property setter:  <property>description</property>.
844    *
845    * @param value
846    *    The new value for this property.
847    *    <br>Can be <jk>null</jk> to unset the property.
848    * @return This object
849    */
850   public SchemaInfo setDescription(String value) {
851      description = value;
852      return this;
853   }
854
855   /**
856    * Bean property setter:  <property>discriminator</property>.
857    *
858    * @param value
859    *    The new value for this property.
860    *    <br>Can be <jk>null</jk> to unset the property.
861    * @return This object
862    */
863   public SchemaInfo setDiscriminator(Discriminator value) {
864      discriminator = value;
865      return this;
866   }
867
868   /**
869    * Bean property setter:  <property>enum</property>.
870    *
871    * @param value
872    *    The new value for this property.
873    *    <br>Can be <jk>null</jk> to unset the property.
874    * @return This object
875    */
876   public SchemaInfo setEnum(Collection<Object> value) {
877      enum_.clear();
878      if (nn(value))
879         enum_.addAll(value);
880      return this;
881   }
882
883   /**
884    * Bean property setter:  <property>example</property>.
885    *
886    * @param value
887    *    The new value for this property.
888    *    <br>Can be <jk>null</jk> to unset the property.
889    * @return This object
890    */
891   public SchemaInfo setExample(Object value) {
892      example = value;
893      return this;
894   }
895
896   /**
897    * Bean property setter:  <property>exclusiveMaximum</property>.
898    *
899    * @param value
900    *    The new value for this property.
901    *    <br>Can be <jk>null</jk> to unset the property.
902    * @return This object
903    */
904   public SchemaInfo setExclusiveMaximum(Boolean value) {
905      exclusiveMaximum = value;
906      return this;
907   }
908
909   /**
910    * Bean property setter:  <property>exclusiveMinimum</property>.
911    *
912    * @param value
913    *    The new value for this property.
914    *    <br>Can be <jk>null</jk> to unset the property.
915    * @return This object
916    */
917   public SchemaInfo setExclusiveMinimum(Boolean value) {
918      exclusiveMinimum = value;
919      return this;
920   }
921
922   /**
923    * Bean property setter:  <property>externalDocs</property>.
924    *
925    * @param value
926    *    The new value for this property.
927    *    <br>Can be <jk>null</jk> to unset the property.
928    * @return This object
929    */
930   public SchemaInfo setExternalDocs(ExternalDocumentation value) {
931      externalDocs = value;
932      return this;
933   }
934
935   /**
936    * Bean property setter:  <property>format</property>.
937    *
938    * @param value
939    *    The new value for this property.
940    *    <br>Can be <jk>null</jk> to unset the property.
941    *    <br>Formats defined by the OAS include:
942    *    <ul>
943    *       <li><js>"int32"</js>
944    *       <li><js>"int64"</js>
945    *       <li><js>"float"</js>
946    *       <li><js>"double"</js>
947    *       <li><js>"byte"</js>
948    *       <li><js>"binary"</js>
949    *       <li><js>"date"</js>
950    *       <li><js>"date-time"</js>
951    *       <li><js>"password"</js>
952    *    </ul>
953    * @return This object
954    */
955   public SchemaInfo setFormat(String value) {
956      format = value;
957      return this;
958   }
959
960   /**
961    * Bean property setter:  <property>items</property>.
962    *
963    * @param value
964    *    The new value for this property.
965    *    <br>Can be <jk>null</jk> to unset the property.
966    * @return This object
967    */
968   public SchemaInfo setItems(Items value) {
969      items = value;
970      return this;
971   }
972
973   /**
974    * Bean property setter:  <property>maximum</property>.
975    *
976    * @param value
977    *    The new value for this property.
978    *    <br>Can be <jk>null</jk> to unset the property.
979    * @return This object
980    */
981   public SchemaInfo setMaximum(Number value) {
982      maximum = value;
983      return this;
984   }
985
986   /**
987    * Bean property setter:  <property>maxItems</property>.
988    *
989    * @param value
990    *    The new value for this property.
991    *    <br>Can be <jk>null</jk> to unset the property.
992    * @return This object
993    */
994   public SchemaInfo setMaxItems(Integer value) {
995      maxItems = value;
996      return this;
997   }
998
999   /**
1000    * Bean property setter:  <property>maxLength</property>.
1001    *
1002    * @param value
1003    *    The new value for this property.
1004    *    <br>Can be <jk>null</jk> to unset the property.
1005    * @return This object
1006    */
1007   public SchemaInfo setMaxLength(Integer value) {
1008      maxLength = value;
1009      return this;
1010   }
1011
1012   /**
1013    * Bean property setter:  <property>maxProperties</property>.
1014    *
1015    * @param value
1016    *    The new value for this property.
1017    *    <br>Can be <jk>null</jk> to unset the property.
1018    * @return This object
1019    */
1020   public SchemaInfo setMaxProperties(Integer value) {
1021      maxProperties = value;
1022      return this;
1023   }
1024
1025   /**
1026    * Bean property setter:  <property>minimum</property>.
1027    *
1028    * @param value
1029    *    The new value for this property.
1030    *    <br>Can be <jk>null</jk> to unset the property.
1031    * @return This object
1032    */
1033   public SchemaInfo setMinimum(Number value) {
1034      minimum = value;
1035      return this;
1036   }
1037
1038   /**
1039    * Bean property setter:  <property>minItems</property>.
1040    *
1041    * @param value
1042    *    The new value for this property.
1043    *    <br>Can be <jk>null</jk> to unset the property.
1044    * @return This object
1045    */
1046   public SchemaInfo setMinItems(Integer value) {
1047      minItems = value;
1048      return this;
1049   }
1050
1051   /**
1052    * Bean property setter:  <property>minLength</property>.
1053    *
1054    * @param value
1055    *    The new value for this property.
1056    *    <br>Can be <jk>null</jk> to unset the property.
1057    * @return This object
1058    */
1059   public SchemaInfo setMinLength(Integer value) {
1060      minLength = value;
1061      return this;
1062   }
1063
1064   /**
1065    * Bean property setter:  <property>minProperties</property>.
1066    *
1067    * @param value
1068    *    The new value for this property.
1069    *    <br>Can be <jk>null</jk> to unset the property.
1070    * @return This object
1071    */
1072   public SchemaInfo setMinProperties(Integer value) {
1073      minProperties = value;
1074      return this;
1075   }
1076
1077   /**
1078    * Bean property setter:  <property>multipleOf</property>.
1079    *
1080    * @param value
1081    *    The new value for this property.
1082    *    <br>Can be <jk>null</jk> to unset the property.
1083    * @return This object
1084    */
1085   public SchemaInfo setMultipleOf(Number value) {
1086      multipleOf = value;
1087      return this;
1088   }
1089
1090   /**
1091    * Bean property setter:  <property>not</property>.
1092    *
1093    * @param value
1094    *    The new value for this property.
1095    *    <br>Can be <jk>null</jk> to unset the property.
1096    * @return This object
1097    */
1098   public SchemaInfo setNot(SchemaInfo value) {
1099      not = value;
1100      return this;
1101   }
1102
1103   /**
1104    * Bean property setter:  <property>nullable</property>.
1105    *
1106    * @param value
1107    *    The new value for this property.
1108    *    <br>Can be <jk>null</jk> to unset the property.
1109    * @return This object
1110    */
1111   public SchemaInfo setNullable(Boolean value) {
1112      nullable = value;
1113      return this;
1114   }
1115
1116   /**
1117    * Bean property setter:  <property>allOf</property>.
1118    *
1119    * @param value
1120    *    The new value for this property.
1121    *    <br>Can be <jk>null</jk> to unset the property.
1122    * @return This object
1123    */
1124   public SchemaInfo setOneOf(Collection<Object> value) {
1125      oneOf.clear();
1126      if (nn(value))
1127         oneOf.addAll(value);
1128      return this;
1129   }
1130
1131   /**
1132    * Bean property setter:  <property>pattern</property>.
1133    *
1134    * <p>
1135    * This string SHOULD be a valid regular expression.
1136    *
1137    * @param value
1138    *    The new value for this property.
1139    *    <br>Can be <jk>null</jk> to unset the property.
1140    * @return This object
1141    */
1142   public SchemaInfo setPattern(String value) {
1143      pattern = value;
1144      return this;
1145   }
1146
1147   /**
1148    * Bean property setter:  <property>properties</property>.
1149    *
1150    * @param value
1151    *    The new value for this property.
1152    *    <br>Can be <jk>null</jk> to unset the property.
1153    * @return This object
1154    */
1155   public SchemaInfo setProperties(Map<String,SchemaInfo> value) {
1156      properties = copyOf(value);
1157      return this;
1158   }
1159
1160   /**
1161    * Bean property setter:  <property>readOnly</property>.
1162    *
1163    * @param value
1164    *    The new value for this property.
1165    *    <br>Can be <jk>null</jk> to unset the property.
1166    * @return This object
1167    */
1168   public SchemaInfo setReadOnly(Boolean value) {
1169      readOnly = value;
1170      return this;
1171   }
1172
1173   /**
1174    * Bean property setter:  <property>$ref</property>.
1175    *
1176    * @param value
1177    *    The new value for this property.
1178    *    <br>Can be <jk>null</jk> to unset the property.
1179    * @return This object
1180    */
1181   @Beanp("$ref")
1182   public SchemaInfo setRef(Object value) {
1183      ref = s(value);
1184      return this;
1185   }
1186
1187   /**
1188    * Bean property setter:  <property>required</property>.
1189    *
1190    * <p>
1191    * The list of required properties.
1192    *
1193    * @param value
1194    *    The new value for this property.
1195    *    <br>Valid values:
1196    *    <ul>
1197    *       <li><js>"http"</js>
1198    *       <li><js>"https"</js>
1199    *       <li><js>"ws"</js>
1200    *       <li><js>"wss"</js>
1201    *    </ul>
1202    *    <br>Can be <jk>null</jk> to unset the property.
1203    * @return This object
1204    */
1205   public SchemaInfo setRequired(Collection<String> value) {
1206      required.clear();
1207      if (nn(value))
1208         required.addAll(value);
1209      return this;
1210   }
1211
1212   /**
1213    * Bean property setter:  <property>title</property>.
1214    *
1215    * @param value
1216    *    The new value for this property.
1217    *    <br>Can be <jk>null</jk> to unset the property.
1218    * @return This object
1219    */
1220   public SchemaInfo setTitle(String value) {
1221      title = value;
1222      return this;
1223   }
1224
1225   /**
1226    * Bean property setter:  <property>type</property>.
1227    *
1228    * @param value
1229    *    The new value for this property.
1230    *    <br>Can be <jk>null</jk> to unset the property.
1231    *    <br>Possible values include:
1232    *    <ul>
1233    *       <li><js>"object"</js>
1234    *       <li><js>"string"</js>
1235    *       <li><js>"number"</js>
1236    *       <li><js>"integer"</js>
1237    *       <li><js>"boolean"</js>
1238    *       <li><js>"array"</js>
1239    *       <li><js>"file"</js>
1240    *    </ul>
1241    * @return This object
1242    */
1243   public SchemaInfo setType(String value) {
1244      type = value;
1245      return this;
1246   }
1247
1248   /**
1249    * Bean property setter:  <property>uniqueItems</property>.
1250    *
1251    * @param value
1252    *    The new value for this property.
1253    *    <br>Can be <jk>null</jk> to unset the property.
1254    * @return This object
1255    */
1256   public SchemaInfo setUniqueItems(Boolean value) {
1257      uniqueItems = value;
1258      return this;
1259   }
1260
1261   /**
1262    * Bean property setter:  <property>WriteOnly</property>.
1263    *
1264    * @param value
1265    *    The new value for this property.
1266    *    <br>Can be <jk>null</jk> to unset the property.
1267    * @return This object
1268    */
1269   public SchemaInfo setWriteOnly(Boolean value) {
1270      writeOnly = value;
1271      return this;
1272   }
1273
1274   /**
1275    * Bean property setter:  <property>xml</property>.
1276    *
1277    * @param value
1278    *    The new value for this property.
1279    *    <br>Can be <jk>null</jk> to unset the property.
1280    * @return This object
1281    */
1282   public SchemaInfo setXml(Xml value) {
1283      xml = value;
1284      return this;
1285   }
1286
1287   @Override /* Overridden from OpenApiElement */
1288   public SchemaInfo strict() {
1289      super.strict();
1290      return this;
1291   }
1292
1293   @Override /* Overridden from OpenApiElement */
1294   public SchemaInfo strict(Object value) {
1295      super.strict(value);
1296      return this;
1297   }
1298}