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.*;
026import org.apache.juneau.annotation.*;
027import org.apache.juneau.common.utils.*;
028import org.apache.juneau.internal.*;
029import org.apache.juneau.marshaller.*;
030
031/**
032 * Describes a single HTTP header.
033 *
034 * <p>
035 * The Header Object follows the structure of the Parameter Object with the following changes: it does not have a 
036 * <c>name</c> field since the header name is specified in the key, and it does not have a <c>required</c> field 
037 * since headers are always optional in HTTP for Swagger 2.0.
038 *
039 * <h5 class='section'>Swagger Specification:</h5>
040 * <p>
041 * The Header Object is composed of the following fields:
042 * <ul class='spaced-list'>
043 *    <li><c>description</c> (string) - A brief description of the header
044 *    <li><c>type</c> (string, REQUIRED) - The type of the header. Values: <js>"string"</js>, <js>"number"</js>, <js>"integer"</js>, <js>"boolean"</js>, <js>"array"</js>
045 *    <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>)
046 *    <li><c>items</c> ({@link Items}) - Required if type is <js>"array"</js>. Describes the type of items in the array
047 *    <li><c>collectionFormat</c> (string) - How multiple values are formatted. Values: <js>"csv"</js>, <js>"ssv"</js>, <js>"tsv"</js>, <js>"pipes"</js>, <js>"multi"</js>
048 *    <li><c>default</c> (any) - The default value
049 *    <li><c>maximum</c> (number), <c>exclusiveMaximum</c> (boolean), <c>minimum</c> (number), <c>exclusiveMinimum</c> (boolean) - Numeric constraints
050 *    <li><c>maxLength</c> (integer), <c>minLength</c> (integer), <c>pattern</c> (string) - String constraints
051 *    <li><c>maxItems</c> (integer), <c>minItems</c> (integer), <c>uniqueItems</c> (boolean) - Array constraints
052 *    <li><c>enum</c> (array) - Possible values for this header
053 *    <li><c>multipleOf</c> (number) - Must be a multiple of this value
054 * </ul>
055 *
056 * <h5 class='section'>Example:</h5>
057 * <p class='bjava'>
058 *    <jc>// Construct using SwaggerBuilder.</jc>
059 *    HeaderInfo <jv>headerInfo</jv> = <jsm>headerInfo</jsm>(<js>"integer"</js>).description(<js>"The number of allowed requests in the current period"</js>);
060 *
061 *    <jc>// Serialize using JsonSerializer.</jc>
062 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>headerInfo</jv>);
063 *
064 *    <jc>// Or just use toString() which does the same as above.</jc>
065 *    <jv>json</jv> = <jv>headerInfo</jv>.toString();
066 * </p>
067 * <p class='bjson'>
068 *    <jc>// Output</jc>
069 *    {
070 *       <js>"description"</js>: <js>"The number of allowed requests in the current period"</js>,
071 *       <js>"type"</js>: <js>"integer"</js>
072 *    }
073 * </p>
074 *
075 * <h5 class='section'>See Also:</h5><ul>
076 *    <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/#header-object">Swagger 2.0 Specification &gt; Header Object</a>
077 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/describing-responses/">Swagger Describing Responses</a>
078 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
079 * </ul>
080 */
081public class HeaderInfo extends SwaggerElement {
082
083   private static final String[] VALID_TYPES = {"string", "number", "integer", "boolean", "array"};
084   private static final String[] VALID_COLLECTION_FORMATS = {"csv","ssv","tsv","pipes","multi"};
085
086   private String
087      description,
088      type,
089      format,
090      collectionFormat,
091      pattern,
092      ref;
093   private Number
094      maximum,
095      minimum,
096      multipleOf;
097   private Integer
098      maxLength,
099      minLength,
100      maxItems,
101      minItems;
102   private Boolean
103      exclusiveMaximum,
104      exclusiveMinimum,
105      uniqueItems;
106   private Items items;
107   private Object _default;  // NOSONAR - Intentional naming.
108   private Set<Object> _enum;  // NOSONAR - Intentional naming.
109   private Object example;
110
111   /**
112    * Default constructor.
113    */
114   public HeaderInfo() {}
115
116   /**
117    * Copy constructor.
118    *
119    * @param copyFrom The object to copy.
120    */
121   public HeaderInfo(HeaderInfo copyFrom) {
122      super(copyFrom);
123
124      this.collectionFormat = copyFrom.collectionFormat;
125      this._default = copyFrom._default;
126      this.description = copyFrom.description;
127      this._enum = copyOf(copyFrom._enum);
128      this.example = copyFrom.example;
129      this.exclusiveMaximum = copyFrom.exclusiveMaximum;
130      this.exclusiveMinimum = copyFrom.exclusiveMinimum;
131      this.format = copyFrom.format;
132      this.items = copyFrom.items == null ? null : copyFrom.items.copy();
133      this.maximum = copyFrom.maximum;
134      this.maxItems = copyFrom.maxItems;
135      this.maxLength = copyFrom.maxLength;
136      this.minimum = copyFrom.minimum;
137      this.minItems = copyFrom.minItems;
138      this.minLength = copyFrom.minLength;
139      this.multipleOf = copyFrom.multipleOf;
140      this.pattern = copyFrom.pattern;
141      this.ref = copyFrom.ref;
142      this.type = copyFrom.type;
143      this.uniqueItems = copyFrom.uniqueItems;
144   }
145
146   /**
147    * Make a deep copy of this object.
148    *
149    * @return A deep copy of this object.
150    */
151   public HeaderInfo copy() {
152      return new HeaderInfo(this);
153   }
154
155   @Override /* Overridden from SwaggerElement */
156   public HeaderInfo strict() {
157      super.strict();
158      return this;
159   }
160
161   //-----------------------------------------------------------------------------------------------------------------
162   // Properties
163   //-----------------------------------------------------------------------------------------------------------------
164
165   /**
166    * Bean property getter:  <property>collectionFormat</property>.
167    *
168    * <p>
169    * Determines the format of the array if type array is used.
170    *
171    * @return The property value, or <jk>null</jk> if it is not set.
172    */
173   public String getCollectionFormat() {
174      return collectionFormat;
175   }
176
177   /**
178    * Bean property setter:  <property>collectionFormat</property>.
179    *
180    * <p>
181    * Determines the format of the array if type array is used.
182    *
183    * @param value
184    *    The new value for this property.
185    *    <br>Valid values:
186    *    <ul>
187    *       <li><js>"csv"</js> (default) - comma separated values <c>foo,bar</c>.
188    *       <li><js>"ssv"</js> - space separated values <c>foo bar</c>.
189    *       <li><js>"tsv"</js> - tab separated values <c>foo\tbar</c>.
190    *       <li><js>"pipes"</js> - pipe separated values <c>foo|bar</c>.
191    *    </ul>
192    *    <br>Can be <jk>null</jk> to unset the property.
193    * @return This object.
194    */
195   public HeaderInfo setCollectionFormat(String value) {
196      if (isStrict() && ! ArrayUtils.contains(value, VALID_COLLECTION_FORMATS))
197         throw new BasicRuntimeException(
198            "Invalid value passed in to setCollectionFormat(String).  Value=''{0}'', valid values={1}",
199            value, Json5.of(VALID_COLLECTION_FORMATS)
200         );
201      collectionFormat = value;
202      return this;
203   }
204
205   /**
206    * Bean property getter:  <property>default</property>.
207    *
208    * <p>
209    * Declares the value of the header that the server will use if none is provided.
210    *
211    * <h5 class='section'>Notes:</h5><ul>
212    *    <li class='note'>
213    *       <js>"default"</js> has no meaning for required items.
214    *    <li class='note'>
215    *       Unlike JSON Schema this value MUST conform to the defined <c>type</c> for the header.
216    * </ul>
217    *
218    * @return The property value, or <jk>null</jk> if it is not set.
219    */
220   public Object getDefault() {
221      return _default;
222   }
223
224   /**
225    * Bean property setter:  <property>default</property>.
226    *
227    * <p>
228    * Declares the value of the header that the server will use if none is provided.
229    *
230    * <h5 class='section'>Notes:</h5><ul>
231    *    <li class='note'>
232    *       <js>"default"</js> has no meaning for required items.
233    *    <li class='note'>
234    *       Unlike JSON Schema this value MUST conform to the defined <c>type</c> for the header.
235    * </ul>
236    *
237    * @param value
238    *    The new value for this property.
239    *    <br>Can be <jk>null</jk> to unset the property.
240    * @return This object.
241    */
242   public HeaderInfo setDefault(Object value) {
243      _default = value;
244      return this;
245   }
246
247   /**
248    * Bean property getter:  <property>description</property>.
249    *
250    * <p>
251    * A short description of the header.
252    *
253    * @return The property value, or <jk>null</jk> if it is not set.
254    */
255   public String getDescription() {
256      return description;
257   }
258
259   /**
260    * Bean property setter:  <property>description</property>.
261    *
262    * <p>
263    * A short description of the header.
264    *
265    * @param value
266    *    The new value for this property.
267    *    <br>Can be <jk>null</jk> to unset the property.
268    * @return This object.
269    */
270   public HeaderInfo setDescription(String value) {
271      description = value;
272      return this;
273   }
274
275   /**
276    * Bean property getter:  <property>enum</property>.
277    *
278    * @return The property value, or <jk>null</jk> if it is not set.
279    */
280   public Set<Object> getEnum() {
281      return _enum;
282   }
283
284   /**
285    * Bean property setter:  <property>enum</property>.
286    *
287    * @param value
288    *    The new value for this property.
289    *    <br>Can be <jk>null</jk> to unset the property.
290    * @return This object.
291    */
292   public HeaderInfo setEnum(Collection<Object> value) {
293      _enum = setFrom(value);
294      return this;
295   }
296
297   /**
298    * Bean property setter:  <property>enum</property>.
299    *
300    * @param value
301    *    The new value for this property.
302    *    <br>Can be <jk>null</jk> to unset the property.
303    * @return This object.
304    */
305   public HeaderInfo setEnum(Object...value) {
306      return setEnum(Arrays.asList(value));
307   }
308
309   /**
310    * Bean property fluent setter:  <property>enum</property>.
311    *
312    * @param value
313    *    The new value for this property.
314    *    <br>Strings can be JSON arrays.
315    * @return This object.
316    */
317   public HeaderInfo addEnum(Object...value) {
318      setEnum(setBuilder(_enum).sparse().add(value).build());
319      return this;
320   }
321
322   /**
323    * Bean property getter:  <property>example</property>.
324    *
325    * @return The property value, or <jk>null</jk> if it is not set.
326    */
327   public Object getExample() {
328      return example;
329   }
330
331   /**
332    * Bean property setter:  <property>example</property>.
333    *
334    * @param value
335    *    The new value for this property.
336    *    <br>Can be <jk>null</jk> to unset the property.
337    * @return This object.
338    */
339   public HeaderInfo setExample(Object value) {
340      example = value;
341      return this;
342   }
343
344   /**
345    * Bean property getter:  <property>exclusiveMaximum</property>.
346    *
347    * @return The property value, or <jk>null</jk> if it is not set.
348    */
349   public Boolean getExclusiveMaximum() {
350      return exclusiveMaximum;
351   }
352
353   /**
354    * Bean property setter:  <property>exclusiveMaximum</property>.
355    *
356    * @param value
357    *    The new value for this property.
358    *    <br>Can be <jk>null</jk> to unset the property.
359    * @return This object.
360    */
361   public HeaderInfo setExclusiveMaximum(Boolean value) {
362      exclusiveMaximum = value;
363      return this;
364   }
365
366   /**
367    * Bean property getter:  <property>exclusiveMinimum</property>.
368    *
369    * @return The property value, or <jk>null</jk> if it is not set.
370    */
371   public Boolean getExclusiveMinimum() {
372      return exclusiveMinimum;
373   }
374
375   /**
376    * Bean property setter:  <property>exclusiveMinimum</property>.
377    *
378    * @param value
379    *    The new value for this property.
380    *    <br>Can be <jk>null</jk> to unset the property.
381    * @return This object.
382    */
383   public HeaderInfo setExclusiveMinimum(Boolean value) {
384      exclusiveMinimum = value;
385      return this;
386   }
387
388   /**
389    * Bean property getter:  <property>format</property>.
390    *
391    * <p>
392    * The extending format for the previously mentioned <c>type</c>.
393    *
394    * @return The property value, or <jk>null</jk> if it is not set.
395    */
396   public String getFormat() {
397      return format;
398   }
399
400   /**
401    * Bean property setter:  <property>format</property>.
402    *
403    * <p>
404    * The extending format for the previously mentioned <c>type</c>.
405    *
406    * @param value
407    *    The new value for this property.
408    *    <br>Can be <jk>null</jk> to unset the property.
409    * @return This object.
410    */
411   public HeaderInfo setFormat(String value) {
412      format = value;
413      return this;
414   }
415
416   /**
417    * Bean property getter:  <property>items</property>.
418    *
419    * <p>
420    * Describes the type of items in the array.
421    *
422    * @return The property value, or <jk>null</jk> if it is not set.
423    */
424   public Items getItems() {
425      return items;
426   }
427
428   /**
429    * Bean property setter:  <property>items</property>.
430    *
431    * <p>
432    * Describes the type of items in the array.
433    *
434    * @param value
435    *    The new value for this property.
436    *    <br>Property value is required if <c>type</c> is <js>"array"</js>.
437    *    <br>Can be <jk>null</jk> to unset the property.
438    * @return This object.
439    */
440   public HeaderInfo setItems(Items value) {
441      items = value;
442      return this;
443   }
444
445   /**
446    * Bean property getter:  <property>maximum</property>.
447    *
448    * @return The property value, or <jk>null</jk> if it is not set.
449    */
450   public Number getMaximum() {
451      return maximum;
452   }
453
454   /**
455    * Bean property setter:  <property>maximum</property>.
456    *
457    * @param value
458    *    The new value for this property.
459    *    <br>Can be <jk>null</jk> to unset the property.
460    * @return This object.
461    */
462   public HeaderInfo setMaximum(Number value) {
463      maximum = value;
464      return this;
465   }
466
467   /**
468    * Bean property getter:  <property>maxItems</property>.
469    *
470    * @return The property value, or <jk>null</jk> if it is not set.
471    */
472   public Integer getMaxItems() {
473      return maxItems;
474   }
475
476   /**
477    * Bean property setter:  <property>maxItems</property>.
478    *
479    * @param value
480    *    The new value for this property.
481    *    <br>Can be <jk>null</jk> to unset the property.
482    * @return This object.
483    */
484   public HeaderInfo setMaxItems(Integer value) {
485      maxItems = value;
486      return this;
487   }
488
489   /**
490    * Bean property getter:  <property>maxLength</property>.
491    *
492    * @return The property value, or <jk>null</jk> if it is not set.
493    */
494   public Integer getMaxLength() {
495      return maxLength;
496   }
497
498   /**
499    * Bean property setter:  <property>maxLength</property>.
500    *
501    * @param value
502    *    The new value for this property.
503    *    <br>Can be <jk>null</jk> to unset the property.
504    * @return This object.
505    */
506   public HeaderInfo setMaxLength(Integer value) {
507      maxLength = value;
508      return this;
509   }
510
511   /**
512    * Bean property getter:  <property>minimum</property>.
513    *
514    * @return The property value, or <jk>null</jk> if it is not set.
515    */
516   public Number getMinimum() {
517      return minimum;
518   }
519
520   /**
521    * Bean property setter:  <property>minimum</property>.
522    *
523    * @param value
524    *    The new value for this property.
525    *    <br>Can be <jk>null</jk> to unset the property.
526    * @return This object.
527    */
528   public HeaderInfo setMinimum(Number value) {
529      minimum = value;
530      return this;
531   }
532
533   /**
534    * Bean property getter:  <property>minItems</property>.
535    *
536    * @return The property value, or <jk>null</jk> if it is not set.
537    */
538   public Integer getMinItems() {
539      return minItems;
540   }
541
542   /**
543    * Bean property setter:  <property>minItems</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 HeaderInfo setMinItems(Integer value) {
551      minItems = value;
552      return this;
553   }
554
555   /**
556    * Bean property getter:  <property>minLength</property>.
557    *
558    * @return The property value, or <jk>null</jk> if it is not set.
559    */
560   public Integer getMinLength() {
561      return minLength;
562   }
563
564   /**
565    * Bean property setter:  <property>minLength</property>.
566    *
567    * @param value
568    *    The new value for this property.
569    *    <br>Can be <jk>null</jk> to unset the property.
570    * @return This object.
571    */
572   public HeaderInfo setMinLength(Integer value) {
573      minLength = value;
574      return this;
575   }
576
577   /**
578    * Bean property getter:  <property>multipleOf</property>.
579    *
580    * @return The property value, or <jk>null</jk> if it is not set.
581    */
582   public Number getMultipleOf() {
583      return multipleOf;
584   }
585
586   /**
587    * Bean property setter:  <property>multipleOf</property>.
588    *
589    * @param value
590    *    The new value for this property.
591    *    <br>Can be <jk>null</jk> to unset the property.
592    * @return This object.
593    */
594   public HeaderInfo setMultipleOf(Number value) {
595      multipleOf = value;
596      return this;
597   }
598
599   /**
600    * Bean property getter:  <property>pattern</property>.
601    *
602    * @return The property value, or <jk>null</jk> if it is not set.
603    */
604   public String getPattern() {
605      return pattern;
606   }
607
608   /**
609    * Bean property setter:  <property>pattern</property>.
610    *
611    * @param value
612    *    The new value for this property.
613    *    <br>This string SHOULD be a valid regular expression.
614    *    <br>Can be <jk>null</jk> to unset the property.
615    * @return This object.
616    */
617   public HeaderInfo setPattern(String value) {
618      pattern = value;
619      return this;
620   }
621
622   /**
623    * Bean property getter:  <property>$ref</property>.
624    *
625    * @return The property value, or <jk>null</jk> if it is not set.
626    */
627   @Beanp("$ref")
628   public String getRef() {
629      return ref;
630   }
631
632   /**
633    * Bean property setter:  <property>$ref</property>.
634    *
635    * @param value
636    *    The new value for this property.
637    *    <br>Can be <jk>null</jk> to unset the property.
638    * @return This object.
639    */
640   @Beanp("$ref")
641   public HeaderInfo setRef(String value) {
642      ref = value;
643      return this;
644   }
645
646   /**
647    * Bean property getter:  <property>type</property>.
648    *
649    * <p>
650    * The type of the object.
651    *
652    * @return The property value, or <jk>null</jk> if it is not set.
653    */
654   public String getType() {
655      return type;
656   }
657
658   /**
659    * Bean property setter:  <property>type</property>.
660    *
661    * <p>
662    * The type of the object.
663    *
664    * @param value
665    *    The new value for this property.
666    *    <br>Property value is required.
667    *    <br>Can be <jk>null</jk> to unset the property.
668    *    <br>Valid values:
669    *    <ul>
670    *       <li><js>"string"</js>
671    *       <li><js>"number"</js>
672    *       <li><js>"integer"</js>
673    *       <li><js>"boolean"</js>
674    *       <li><js>"array"</js>
675    *    </ul>
676    * @return This object.
677    */
678   public HeaderInfo setType(String value) {
679      if (isStrict() && ! ArrayUtils.contains(value, VALID_TYPES))
680         throw new BasicRuntimeException(
681            "Invalid value passed in to setType(String).  Value=''{0}'', valid values={1}",
682            value, Json5.of(VALID_TYPES)
683         );
684      type = value;
685      return this;
686   }
687
688   /**
689    * Bean property getter:  <property>uniqueItems</property>.
690    *
691    * @return The property value, or <jk>null</jk> if it is not set.
692    */
693   public Boolean getUniqueItems() {
694      return uniqueItems;
695   }
696
697   /**
698    * Bean property setter:  <property>uniqueItems</property>.
699    *
700    * @param value
701    *    The new value for this property.
702    *    <br>Can be <jk>null</jk> to unset the property.
703    * @return This object.
704    */
705   public HeaderInfo setUniqueItems(Boolean value) {
706      uniqueItems = value;
707      return this;
708   }
709
710   @Override /* Overridden from SwaggerElement */
711   public <T> T get(String property, Class<T> type) {
712      assertArgNotNull("property", property);
713      return switch (property) {
714         case "collectionFormat" -> toType(getCollectionFormat(), type);
715         case "default" -> toType(getDefault(), type);
716         case "description" -> (T)getDescription();
717         case "enum" -> toType(getEnum(), type);
718         case "example" -> toType(getExample(), type);
719         case "exclusiveMaximum" -> toType(getExclusiveMaximum(), type);
720         case "exclusiveMinimum" -> toType(getExclusiveMinimum(), type);
721         case "format" -> toType(getFormat(), type);
722         case "items" -> toType(getItems(), type);
723         case "maximum" -> toType(getMaximum(), type);
724         case "maxItems" -> toType(getMaxItems(), type);
725         case "maxLength" -> toType(getMaxLength(), type);
726         case "minimum" -> toType(getMinimum(), type);
727         case "minItems" -> toType(getMinItems(), type);
728         case "minLength" -> toType(getMinLength(), type);
729         case "multipleOf" -> toType(getMultipleOf(), type);
730         case "pattern" -> toType(getPattern(), type);
731         case "$ref" -> toType(getRef(), type);
732         case "type" -> toType(getType(), type);
733         case "uniqueItems" -> toType(getUniqueItems(), type);
734         default -> super.get(property, type);
735      };
736   }
737
738   @Override /* Overridden from SwaggerElement */
739   public HeaderInfo set(String property, Object value) {
740      assertArgNotNull("property", property);
741      return switch (property) {
742         case "collectionFormat" -> setCollectionFormat(Utils.s(value));
743         case "default" -> setDefault(value);
744         case "description" -> setDescription(Utils.s(value));
745         case "enum" -> setEnum(setBuilder(Object.class).sparse().addAny(value).build());
746         case "example" -> setExample(value);
747         case "exclusiveMaximum" -> setExclusiveMaximum(toBoolean(value));
748         case "exclusiveMinimum" -> setExclusiveMinimum(toBoolean(value));
749         case "format" -> setFormat(Utils.s(value));
750         case "items" -> setItems(toType(value, Items.class));
751         case "maximum" -> setMaximum(toNumber(value));
752         case "maxItems" -> setMaxItems(toInteger(value));
753         case "maxLength" -> setMaxLength(toInteger(value));
754         case "minimum" -> setMinimum(toNumber(value));
755         case "minItems" -> setMinItems(toInteger(value));
756         case "minLength" -> setMinLength(toInteger(value));
757         case "multipleOf" -> setMultipleOf(toNumber(value));
758         case "pattern" -> setPattern(Utils.s(value));
759         case "$ref" -> setRef(Utils.s(value));
760         case "type" -> setType(Utils.s(value));
761         case "uniqueItems" -> setUniqueItems(toBoolean(value));
762         default -> {
763            super.set(property, value);
764            yield this;
765         }
766      };
767   }
768
769   @Override /* Overridden from SwaggerElement */
770   public Set<String> keySet() {
771      var s = setBuilder(String.class)
772         .addIf(collectionFormat != null, "collectionFormat")
773         .addIf(_default != null, "default")
774         .addIf(description != null, "description")
775         .addIf(_enum != null, "enum")
776         .addIf(example != null, "example")
777         .addIf(exclusiveMaximum != null, "exclusiveMaximum")
778         .addIf(exclusiveMinimum != null, "exclusiveMinimum")
779         .addIf(format != null, "format")
780         .addIf(items != null, "items")
781         .addIf(maximum != null, "maximum")
782         .addIf(maxItems != null, "maxItems")
783         .addIf(maxLength != null, "maxLength")
784         .addIf(minimum != null, "minimum")
785         .addIf(minItems != null, "minItems")
786         .addIf(minLength != null, "minLength")
787         .addIf(multipleOf != null, "multipleOf")
788         .addIf(pattern != null, "pattern")
789         .addIf(ref != null, "$ref")
790         .addIf(type != null, "type")
791         .addIf(uniqueItems != null, "uniqueItems")
792         .build();
793      return new MultiSet<>(s, super.keySet());
794   }
795
796   /**
797    * Resolves any <js>"$ref"</js> attributes in this element.
798    *
799    * @param swagger The swagger document containing the definitions.
800    * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops.
801    * @param maxDepth
802    *    The maximum depth to resolve references.
803    *    <br>After that level is reached, <c>$ref</c> references will be left alone.
804    *    <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex.
805    * @return
806    *    This object with references resolved.
807    *    <br>May or may not be the same object.
808    */
809   public HeaderInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) {
810
811      if (ref != null) {
812         if (refStack.contains(ref) || refStack.size() >= maxDepth)
813            return this;
814         refStack.addLast(ref);
815         var r = swagger.findRef(ref, HeaderInfo.class).resolveRefs(swagger, refStack, maxDepth);
816         refStack.removeLast();
817         return r;
818      }
819
820      if (items != null)
821         items = items.resolveRefs(swagger, refStack, maxDepth);
822
823      return this;
824   }
825}