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