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