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