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.ClassUtils.*;
021import static org.apache.juneau.internal.CollectionUtils.*;
022import static org.apache.juneau.internal.CollectionUtils.map;
023import static org.apache.juneau.internal.ConverterUtils.*;
024
025import java.util.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.collections.*;
029import org.apache.juneau.common.utils.*;
030import org.apache.juneau.internal.*;
031import org.apache.juneau.json.*;
032import org.apache.juneau.objecttools.*;
033
034/**
035 * This is the root document object for the Swagger 2.0 API specification.
036 *
037 * <p>
038 * The Swagger Object is the root document that describes an entire API. It contains metadata about the API,
039 * available paths and operations, parameters, responses, security definitions, and other information. This is
040 * the Swagger 2.0 specification (predecessor to OpenAPI 3.0).
041 *
042 * <h5 class='section'>Swagger Specification:</h5>
043 * <p>
044 * The Swagger Object is composed of the following fields:
045 * <ul class='spaced-list'>
046 *    <li><c>swagger</c> (string, REQUIRED) - The Swagger Specification version (must be <js>"2.0"</js>)
047 *    <li><c>info</c> ({@link Info}, REQUIRED) - Provides metadata about the API
048 *    <li><c>host</c> (string) - The host (name or IP) serving the API
049 *    <li><c>basePath</c> (string) - The base path on which the API is served (relative to host)
050 *    <li><c>schemes</c> (array of string) - The transfer protocols of the API (e.g., <js>"http"</js>, <js>"https"</js>)
051 *    <li><c>consumes</c> (array of string) - A list of MIME types the APIs can consume
052 *    <li><c>produces</c> (array of string) - A list of MIME types the APIs can produce
053 *    <li><c>paths</c> (map of {@link OperationMap}, REQUIRED) - The available paths and operations for the API
054 *    <li><c>definitions</c> (map of {@link SchemaInfo}) - Schema definitions that can be referenced
055 *    <li><c>parameters</c> (map of {@link ParameterInfo}) - Parameters definitions that can be referenced
056 *    <li><c>responses</c> (map of {@link ResponseInfo}) - Response definitions that can be referenced
057 *    <li><c>securityDefinitions</c> (map of {@link SecurityScheme}) - Security scheme definitions
058 *    <li><c>security</c> (array of map) - Security requirements applied to all operations
059 *    <li><c>tags</c> (array of {@link Tag}) - A list of tags used by the specification with additional metadata
060 *    <li><c>externalDocs</c> ({@link ExternalDocumentation}) - Additional external documentation
061 * </ul>
062 *
063 * <h5 class='section'>Example:</h5>
064 * <p class='bjava'>
065 *    <jc>// Create a Swagger document</jc>
066 *    Swagger <jv>doc</jv> = <jk>new</jk> Swagger()
067 *       .setSwagger(<js>"2.0"</js>)
068 *       .setInfo(
069 *          <jk>new</jk> Info()
070 *             .setTitle(<js>"Pet Store API"</js>)
071 *             .setVersion(<js>"1.0.0"</js>)
072 *       )
073 *       .setHost(<js>"petstore.swagger.io"</js>)
074 *       .setBasePath(<js>"/v2"</js>)
075 *       .setSchemes(<js>"https"</js>)
076 *       .addPath(<js>"/pets"</js>, <js>"get"</js>,
077 *          <jk>new</jk> Operation()
078 *             .setSummary(<js>"List all pets"</js>)
079 *             .addResponse(<js>"200"</js>, <jk>new</jk> ResponseInfo(<js>"Success"</js>))
080 *       );
081 * </p>
082 *
083 * <h5 class='section'>See Also:</h5><ul>
084 *    <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/">Swagger 2.0 Specification</a>
085 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/basic-structure/">Swagger Basic Structure</a>
086 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
087 * </ul>
088 */
089public class Swagger extends SwaggerElement {
090
091   /** Represents a null swagger */
092   public static final Swagger NULL = new Swagger();
093
094   private static final Comparator<String> PATH_COMPARATOR = (o1, o2) -> o1.replace('{', '@').compareTo(o2.replace('{', '@'));
095
096   private String
097      swagger = "2.0",  // NOSONAR - Intentional naming.
098      host,
099      basePath;
100   private Info info;
101   private ExternalDocumentation externalDocs;
102   private Set<String> schemes;
103   private Set<MediaType>
104      consumes,
105      produces;
106   private Set<Tag> tags;
107   private List<Map<String,List<String>>> security;
108   private Map<String,JsonMap> definitions;
109   private Map<String,ParameterInfo> parameters;
110   private Map<String,ResponseInfo> responses;
111   private Map<String,SecurityScheme> securityDefinitions;
112   private Map<String,OperationMap> paths;
113
114   /**
115    * Default constructor.
116    */
117   public Swagger() {}
118
119   /**
120    * Copy constructor.
121    *
122    * @param copyFrom The object to copy.
123    */
124   public Swagger(Swagger copyFrom) {
125      super(copyFrom);
126
127      this.basePath = copyFrom.basePath;
128      this.consumes = copyOf(copyFrom.consumes);
129      this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy();
130      this.host = copyFrom.host;
131      this.info = copyFrom.info == null ? null : copyFrom.info.copy();
132      this.produces = copyOf(copyFrom.produces);
133      this.schemes = copyOf(copyFrom.schemes);
134      this.swagger = copyFrom.swagger;
135
136      // TODO - Definitions are not deep copied, so they should not contain references.
137      this.definitions = copyOf(copyFrom.definitions, JsonMap::new);
138
139      this.paths = copyOf(copyFrom.paths, v -> {
140         var m = new OperationMap();
141         v.forEach((k2,v2) -> m.put(k2, v2.copy()));
142         return m;
143      });
144
145      this.parameters = copyOf(copyFrom.parameters, ParameterInfo::copy);
146      this.responses = copyOf(copyFrom.responses, ResponseInfo::copy);
147      this.securityDefinitions = copyOf(copyFrom.securityDefinitions, SecurityScheme::copy);
148
149      this.security = copyOf(copyFrom.security, x -> {
150         Map<String,List<String>> m2 = map();
151         x.forEach((k,v) -> m2.put(k, copyOf(v)));
152         return m2;
153      });
154
155      this.tags = copyOf(copyFrom.tags, x -> x.copy());
156   }
157
158   /**
159    * Make a deep copy of this object.
160    *
161    * @return A deep copy of this object.
162    */
163   public Swagger copy() {
164      return new Swagger(this);
165   }
166
167   //-----------------------------------------------------------------------------------------------------------------
168   // Properties
169   //-----------------------------------------------------------------------------------------------------------------
170
171   /**
172    * Bean property getter:  <property>basePath</property>.
173    *
174    * <p>
175    * The base path on which the API is served, which is relative to the <c>host</c>.
176    *
177    * @return The property value, or <jk>null</jk> if it is not set.
178    */
179   public String getBasePath() {
180      return basePath;
181   }
182
183   /**
184    * Bean property setter:  <property>basePath</property>.
185    *
186    * <p>
187    * The base path on which the API is served, which is relative to the <c>host</c>.
188    *
189    * @param value
190    *    The new value for this property.
191    *    <br>If it is not included, the API is served directly under the <c>host</c>.
192    *    <br>The value MUST start with a leading slash (/).
193    *    <br>The <c>basePath</c> does not support <a class="doclink" href="https://swagger.io/specification/v2#pathTemplating">path templating</a>.
194    *    <br>Can be <jk>null</jk> to unset the property.
195    * @return This object.
196    */
197   public Swagger setBasePath(String value) {
198      basePath = value;
199      return this;
200   }
201
202   /**
203    * Bean property getter:  <property>consumes</property>.
204    *
205    * <p>
206    * A list of MIME types the APIs can consume.
207    *
208    * @return The property value, or <jk>null</jk> if it is not set.
209    */
210   public Set<MediaType> getConsumes() {
211      return consumes;
212   }
213
214   /**
215    * Bean property setter:  <property>consumes</property>.
216    *
217    * <p>
218    * A list of MIME types the APIs can consume.
219    *
220    * @param value
221    *    The new value for this property.
222    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
223    *    <br>Can be <jk>null</jk> to unset the property.
224    * @return This object.
225    */
226   public Swagger setConsumes(Collection<MediaType> value) {
227      consumes = setFrom(value);
228      return this;
229   }
230
231   /**
232    * Bean property appender:  <property>consumes</property>.
233    *
234    * <p>
235    * A list of MIME types the APIs can consume.
236    *
237    * @param values
238    *    The values to add to this property.
239    *    <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
240    *    <br>Ignored if <jk>null</jk>.
241    * @return This object.
242    */
243   public Swagger addConsumes(MediaType...values) {
244      consumes = setBuilder(consumes).sparse().add(values).build();
245      return this;
246   }
247
248   /**
249    * Bean property appender:  <property>consumes</property>.
250    *
251    * <p>
252    * A list of MIME types the APIs can consume.
253    *
254    * @param values
255    *    The values to add to this property.
256    *    <br>Ignored if <jk>null</jk>.
257    * @return This object.
258    */
259   public Swagger addConsumes(Collection<MediaType> values) {
260      consumes = setBuilder(consumes).sparse().addAll(values).build();
261      return this;
262   }
263
264   /**
265    * Bean property fluent setter:  <property>consumes</property>.
266    *
267    * <p>
268    * A list of MIME types the APIs can consume.
269    *
270    * @param value
271    *    The values to set on this property.
272    * @return This object.
273    */
274   public Swagger setConsumes(MediaType...value) {
275      setConsumes(setBuilder(MediaType.class).sparse().add(value).build());
276      return this;
277   }
278
279   /**
280    * Bean property getter:  <property>definitions</property>.
281    *
282    * <p>
283    * An object to hold data types produced and consumed by operations.
284    *
285    * @return The property value, or <jk>null</jk> if it is not set.
286    */
287   public Map<String,JsonMap> getDefinitions() {
288      return definitions;
289   }
290
291   /**
292    * Bean property setter:  <property>definitions</property>.
293    *
294    * <p>
295    * An object to hold data types produced and consumed by operations.
296    *
297    * @param value
298    *    The new value for this property.
299    *    <br>Can be <jk>null</jk> to unset the property.
300    * @return This object.
301    */
302   public Swagger setDefinitions(Map<String,JsonMap> value) {
303      definitions = copyOf(value);
304      return this;
305   }
306
307   /**
308    * Bean property appender:  <property>definitions</property>.
309    *
310    * <p>
311    * Adds a single value to the <property>definitions</property> property.
312    *
313    * @param name A definition name.  Must not be <jk>null</jk>.
314    * @param schema The schema that the name defines.  Must not be <jk>null</jk>.
315    * @return This object.
316    */
317   public Swagger addDefinition(String name, JsonMap schema) {
318      assertArgNotNull("name", name);
319      assertArgNotNull("schema", schema);
320      definitions = mapBuilder(definitions).sparse().add(name, schema).build();
321      return this;
322   }
323
324   /**
325    * Bean property getter:  <property>externalDocs</property>.
326    *
327    * <p>
328    * Additional external documentation.
329    *
330    * @return The property value, or <jk>null</jk> if it is not set.
331    */
332   public ExternalDocumentation getExternalDocs() {
333      return externalDocs;
334   }
335
336   /**
337    * Bean property setter:  <property>externalDocs</property>.
338    *
339    * <p>
340    * Additional external documentation.
341    *
342    * @param value
343    *    The new value for this property.
344    *    <br>Can be <jk>null</jk> to unset the property.
345    * @return This object.
346    */
347   public Swagger setExternalDocs(ExternalDocumentation value) {
348      externalDocs = value;
349      return this;
350   }
351
352   /**
353    * Bean property getter:  <property>host</property>.
354    *
355    * <p>
356    * The host (name or IP) serving the API.
357    *
358    * @return The property value, or <jk>null</jk> if it is not set.
359    */
360   public String getHost() {
361      return host;
362   }
363
364   /**
365    * Bean property setter:  <property>host</property>.
366    *
367    * <p>
368    * The host (name or IP) serving the API.
369    *
370    * @param value
371    *    The new value for this property.
372    *    <br>This MUST be the host only and does not include the scheme nor sub-paths.
373    *    <br>It MAY include a port.
374    *    <br>If the host is not included, the host serving the documentation is to be used (including the port).
375    *    <br>The host does not support <a class="doclink" href="https://swagger.io/specification/v2#pathTemplating">path templating</a>
376    *    <br>Can be <jk>null</jk> to unset the property.
377    * @return This object.
378    */
379   public Swagger setHost(String value) {
380      host = value;
381      return this;
382   }
383
384   /**
385    * Bean property getter:  <property>info</property>.
386    *
387    * <p>
388    * Provides metadata about the API.
389    *
390    * @return The property value, or <jk>null</jk> if it is not set.
391    */
392   public Info getInfo() {
393      return info;
394   }
395
396   /**
397    * Bean property setter:  <property>info</property>.
398    *
399    * <p>
400    * Provides metadata about the API.
401    *
402    * @param value
403    *    The new value for this property.
404    *    <br>Property value is required.
405    *    <br>Can be <jk>null</jk> to unset the property.
406    * @return This object.
407    */
408   public Swagger setInfo(Info value) {
409      info = value;
410      return this;
411   }
412
413   /**
414    * Bean property getter:  <property>parameters</property>.
415    *
416    * <p>
417    * An object to hold parameters that can be used across operations.
418    *
419    * @return The property value, or <jk>null</jk> if it is not set.
420    */
421   public Map<String,ParameterInfo> getParameters() {
422      return parameters;
423   }
424
425   /**
426    * Bean property setter:  <property>parameters</property>.
427    *
428    * <p>
429    * An object to hold parameters that can be used across operations.
430    *
431    * @param value
432    *    The new value for this property.
433    *    <br>Can be <jk>null</jk> to unset the property.
434    * @return This object.
435    */
436   public Swagger setParameters(Map<String,ParameterInfo> value) {
437      parameters = copyOf(value);
438      return this;
439   }
440
441   /**
442    * Bean property appender:  <property>parameters</property>.
443    *
444    * <p>
445    * Adds a single value to the <property>parameter</property> property.
446    *
447    * @param name The parameter name.  Must not be <jk>null</jk>.
448    * @param parameter The parameter definition.  Must not be <jk>null</jk>.
449    * @return This object.
450    */
451   public Swagger addParameter(String name, ParameterInfo parameter) {
452      assertArgNotNull("name", name);
453      assertArgNotNull("parameter", parameter);
454      parameters = mapBuilder(parameters).sparse().add(name, parameter).build();
455      return this;
456   }
457
458   /**
459    * Bean property getter:  <property>paths</property>.
460    *
461    * <p>
462    * The available paths and operations for the API.
463    *
464    * @return The property value, or <jk>null</jk> if it is not set.
465    */
466   public Map<String,OperationMap> getPaths() {
467      return paths;
468   }
469
470   /**
471    * Bean property setter:  <property>paths</property>.
472    *
473    * <p>
474    * The available paths and operations for the API.
475    *
476    * @param value
477    *    The new value for this property.
478    *    <br>Property value is required.
479    *    <br>Can be <jk>null</jk> to unset the property.
480    * @return This object.
481    */
482   public Swagger setPaths(Map<String,OperationMap> value) {
483      paths = mapBuilder(String.class,OperationMap.class).sparse().sorted(PATH_COMPARATOR).addAll(value).build();
484      return this;
485   }
486
487   /**
488    * Bean property appender:  <property>paths</property>.
489    *
490    * <p>
491    * Adds a single value to the <property>paths</property> property.
492    *
493    * @param path The path template.  Must not be <jk>null</jk>.
494    * @param methodName The HTTP method name.  Must not be <jk>null</jk>.
495    * @param operation The operation that describes the path.  Must not be <jk>null</jk>.
496    * @return This object.
497    */
498   public Swagger addPath(String path, String methodName, Operation operation) {
499      assertArgNotNull("path", path);
500      assertArgNotNull("methodName", methodName);
501      assertArgNotNull("operation", operation);
502      if (paths == null)
503         paths = new TreeMap<>(PATH_COMPARATOR);
504      getPaths().computeIfAbsent(path, k -> new OperationMap()).put(methodName, operation);
505      return this;
506   }
507
508   /**
509    * Bean property getter:  <property>produces</property>.
510    *
511    * <p>
512    * A list of MIME types the APIs can produce.
513    *
514    * @return The property value, or <jk>null</jk> if it is not set.
515    */
516   public Set<MediaType> getProduces() {
517      return produces;
518   }
519
520   /**
521    * Bean property setter:  <property>produces</property>.
522    *
523    * <p>
524    * A list of MIME types the APIs can produce.
525    *
526    * @param value
527    *    The new value for this property.
528    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
529    *    <br>Can be <jk>null</jk> to unset the property.
530    * @return This object.
531    */
532   public Swagger setProduces(Collection<MediaType> value) {
533      produces = setFrom(value);
534      return this;
535   }
536
537   /**
538    * Adds one or more values to the <property>produces</property> property.
539    *
540    * <p>
541    * A list of MIME types the APIs can produce.
542    *
543    * @param values
544    *    The values to add to this property.
545    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
546    *    <br>Can be <jk>null</jk> to unset the property.
547    * @return This object.
548    */
549   public Swagger addProduces(MediaType...values) {
550      produces = setBuilder(produces).sparse().add(values).build();
551      return this;
552   }
553
554   /**
555    * Bean property appender:  <property>produces</property>.
556    *
557    * <p>
558    * A list of MIME types the APIs can produce.
559    *
560    * @param values
561    *    The values to add to this property.
562    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
563    *    <br>Ignored if <jk>null</jk>.
564    * @return This object.
565    */
566   public Swagger addProduces(Collection<MediaType> values) {
567      produces = setBuilder(produces).sparse().addAll(values).build();
568      return this;
569   }
570
571   /**
572    * Bean property fluent setter:  <property>produces</property>.
573    *
574    * <p>
575    * A list of MIME types the APIs can produce.
576    *
577    * @param value
578    *    The new value for this property.
579    * @return This object.
580    */
581   public Swagger setProduces(MediaType...value) {
582      setProduces(setBuilder(MediaType.class).sparse().add(value).build());
583      return this;
584   }
585
586   /**
587    * Bean property getter:  <property>responses</property>.
588    *
589    * <p>
590    * An object to hold responses that can be used across operations.
591    *
592    * @return The property value, or <jk>null</jk> if it is not set.
593    */
594   public Map<String,ResponseInfo> getResponses() {
595      return responses;
596   }
597
598   /**
599    * Bean property setter:  <property>responses</property>.
600    *
601    * <p>
602    * An object to hold responses that can be used across operations.
603    *
604    * @param value
605    *    The new value for this property.
606    *    <br>Can be <jk>null</jk> to unset the property.
607    * @return This object.
608    */
609   public Swagger setResponses(Map<String,ResponseInfo> value) {
610      responses = copyOf(value);
611      return this;
612   }
613
614   /**
615    * Bean property appender:  <property>responses</property>.
616    *
617    * <p>
618    * Adds a single value to the <property>responses</property> property.
619    *
620    * @param name The response name.  Must not be <jk>null</jk>.
621    * @param response The response definition.  Must not be <jk>null</jk>.
622    * @return This object.
623    */
624   public Swagger addResponse(String name, ResponseInfo response) {
625      assertArgNotNull("name", name);
626      assertArgNotNull("response", response);
627      responses = mapBuilder(responses).sparse().add(name, response).build();
628      return this;
629   }
630
631   /**
632    * Bean property getter:  <property>schemes</property>.
633    *
634    * <p>
635    * The transfer protocol of the API.
636    *
637    * @return The property value, or <jk>null</jk> if it is not set.
638    */
639   public Set<String> getSchemes() {
640      return schemes;
641   }
642
643   /**
644    * Bean property setter:  <property>schemes</property>.
645    *
646    * <p>
647    * The transfer protocol of the API.
648    *
649    * @param value
650    *    The new value for this property.
651    *    <br>Valid values:
652    *    <ul>
653    *       <li><js>"http"</js>
654    *       <li><js>"https"</js>
655    *       <li><js>"ws"</js>
656    *       <li><js>"wss"</js>
657    *    </ul>
658    *    <br>Can be <jk>null</jk> to unset the property.
659    * @return This object.
660    */
661   public Swagger setSchemes(Collection<String> value) {
662      schemes = setFrom(value);
663      return this;
664   }
665
666   /**
667    * Bean property appender:  <property>schemes</property>.
668    *
669    * <p>
670    * The transfer protocol of the API.
671    *
672    * @param values
673    *    The values to add to this property.
674    *    <br>Valid values:
675    *    <ul>
676    *       <li><js>"http"</js>
677    *       <li><js>"https"</js>
678    *       <li><js>"ws"</js>
679    *       <li><js>"wss"</js>
680    *    </ul>
681    *    <br>Ignored if <jk>null</jk>.
682    * @return This object.
683    */
684   public Swagger addSchemes(String...values) {
685      schemes = setBuilder(schemes).sparse().add(values).build();
686      return this;
687   }
688
689   /**
690    * Bean property appender:  <property>schemes</property>.
691    *
692    * <p>
693    * The transfer protocol of the API.
694    *
695    * @param values
696    *    The values to add to this property.
697    *    <br>Valid values:
698    *    <ul>
699    *       <li><js>"http"</js>
700    *       <li><js>"https"</js>
701    *       <li><js>"ws"</js>
702    *       <li><js>"wss"</js>
703    *    </ul>
704    *    <br>Ignored if <jk>null</jk>.
705    * @return This object.
706    */
707   public Swagger addSchemes(Collection<String> values) {
708      schemes = setBuilder(schemes).sparse().addAll(values).build();
709      return this;
710   }
711
712   /**
713    * Bean property fluent setter:  <property>schemes</property>.
714    *
715    * <p>
716    * The transfer protocol of the API.
717    *
718    * @param value
719    *    The new value for this property.
720    *    <br>Strings can be JSON arrays.
721    * @return This object.
722    */
723   public Swagger setSchemes(String...value) {
724      setSchemes(setBuilder(String.class).sparse().addJson(value).build());
725      return this;
726   }
727
728   /**
729    * Bean property getter:  <property>security</property>.
730    *
731    * <p>
732    * A declaration of which security schemes are applied for the API as a whole.
733    *
734    * @return The property value, or <jk>null</jk> if it is not set.
735    */
736   public List<Map<String,List<String>>> getSecurity() {
737      return security;
738   }
739
740   /**
741    * Bean property setter:  <property>security</property>.
742    *
743    * <p>
744    * A declaration of which security schemes are applied for the API as a whole.
745    *
746    * @param value
747    *    The new value for this property.
748    *    <br>Can be <jk>null</jk> to unset the property.
749    * @return This object.
750    */
751   public Swagger setSecurity(Collection<Map<String,List<String>>> value) {
752      security = listFrom(value);
753      return this;
754   }
755
756   /**
757    * Bean property appender:  <property>security</property>.
758    *
759    * <p>
760    * Adds a single value to the <property>securityDefinitions</property> property.
761    *
762    * @param scheme The security scheme that applies to this operation  Must not be <jk>null</jk>.
763    * @param alternatives
764    *    The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements).
765    * @return This object.
766    */
767   public Swagger addSecurity(String scheme, String...alternatives) {
768      assertArgNotNull("scheme", scheme);
769      Map<String,List<String>> m = map();
770      m.put(scheme, alist(alternatives));
771      security = listBuilder(security).sparse().addAll(Collections.singleton(m)).build();
772      return this;
773   }
774
775   /**
776    * Bean property fluent setter:  <property>security</property>.
777    *
778    * <p>
779    * A declaration of which security schemes are applied for the API as a whole.
780    *
781    * @param values
782    *    The values to add to this property.
783    *    <br>Ignored if <jk>null</jk>.
784    * @return This object.
785    */
786   public Swagger addSecurity(Collection<Map<String,List<String>>> values) {
787      security = listBuilder(security).sparse().addAll(values).build();
788      return this;
789   }
790
791   /**
792    * Bean property getter:  <property>securityDefinitions</property>.
793    *
794    * <p>
795    * Security scheme definitions that can be used across the specification.
796    *
797    * @return The property value, or <jk>null</jk> if it is not set.
798    */
799   public Map<String,SecurityScheme> getSecurityDefinitions() {
800      return securityDefinitions;
801   }
802
803   /**
804    * Bean property setter:  <property>securityDefinitions</property>.
805    *
806    * <p>
807    * Security scheme definitions that can be used across the specification.
808    *
809    * @param value
810    *    The new value for this property.
811    *    <br>Can be <jk>null</jk> to unset the property.
812    * @return This object.
813    */
814   public Swagger setSecurityDefinitions(Map<String,SecurityScheme> value) {
815      securityDefinitions = copyOf(value);
816      return this;
817   }
818
819   /**
820    * Bean property appender:  <property>securityDefinitions</property>.
821    *
822    * <p>
823    * Adds a single value to the <property>securityDefinitions</property> property.
824    *
825    * @param name A security name.  Must not be <jk>null</jk>.
826    * @param securityScheme A security schema.  Must not be <jk>null</jk>.
827    * @return This object.
828    */
829   public Swagger addSecurityDefinition(String name, SecurityScheme securityScheme) {
830      assertArgNotNull("name", name);
831      assertArgNotNull("securityScheme", securityScheme);
832      securityDefinitions = mapBuilder(securityDefinitions).sparse().add(name, securityScheme).build();
833      return this;
834   }
835
836   /**
837    * Bean property getter:  <property>swagger</property>.
838    *
839    * <p>
840    * Specifies the Swagger Specification version being used.
841    *
842    * @return The property value, or <jk>null</jk> if it is not set.
843    */
844   public String getSwagger() {
845      return swagger;
846   }
847
848   /**
849    * Bean property setter:  <property>swagger</property>.
850    *
851    * <p>
852    * Specifies the Swagger Specification version being used.
853    *
854    * @param value
855    *    The new value for this property.
856    *    <br>Property value is required.
857    *    <br>Can be <jk>null</jk> to unset the property.
858    * @return This object.
859    */
860   public Swagger setSwagger(String value) {
861      swagger = value;
862      return this;
863   }
864
865   /**
866    * Bean property getter:  <property>tags</property>.
867    *
868    * <p>
869    * A list of tags used by the specification with additional metadata.
870    *
871    * @return The property value, or <jk>null</jk> if it is not set.
872    */
873   public Set<Tag> getTags() {
874      return tags;
875   }
876
877   /**
878    * Bean property setter:  <property>tags</property>.
879    *
880    * <p>
881    * A list of tags used by the specification with additional metadata.
882    *
883    * @param value
884    *    The new value for this property.
885    *    <br>The order of the tags can be used to reflect on their order by the parsing tools.
886    *    <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared.
887    *    <br>The tags that are not declared may be organized randomly or based on the tools' logic.
888    *    <br>Each tag name in the list MUST be unique.
889    *    <br>Can be <jk>null</jk> to unset the property.
890    * @return This object.
891    */
892   public Swagger setTags(Collection<Tag> value) {
893      tags = setFrom(value);
894      return this;
895   }
896
897   /**
898    * Bean property setter:  <property>tags</property>.
899    *
900    * <p>
901    * A list of tags used by the specification with additional metadata.
902    *
903    * @param value
904    *    The new value for this property.
905    *    <br>The order of the tags can be used to reflect on their order by the parsing tools.
906    *    <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared.
907    *    <br>The tags that are not declared may be organized randomly or based on the tools' logic.
908    *    <br>Each tag name in the list MUST be unique.
909    *    <br>Ignored if <jk>null</jk>.
910    * @return This object.
911    */
912   public Swagger setTags(Tag...value) {
913      setTags(setBuilder(Tag.class).sparse().add(value).build());
914      return this;
915   }
916
917   /**
918    * Bean property appender:  <property>tags</property>.
919    *
920    * <p>
921    * A list of tags used by the specification with additional metadata.
922    *
923    * @param values
924    *    The values to add to this property.
925    *    <br>The order of the tags can be used to reflect on their order by the parsing tools.
926    *    <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared.
927    *    <br>The tags that are not declared may be organized randomly or based on the tools' logic.
928    *    <br>Each tag name in the list MUST be unique.
929    *    <br>Ignored if <jk>null</jk>.
930    * @return This object.
931    */
932   public Swagger addTags(Tag...values) {
933      tags = setBuilder(tags).sparse().add(values).build();
934      return this;
935   }
936
937   /**
938    * Bean property appender:  <property>tags</property>.
939    *
940    * <p>
941    * A list of tags used by the specification with additional metadata.
942    *
943    * @param values
944    *    The values to add to this property.
945    *    <br>The order of the tags can be used to reflect on their order by the parsing tools.
946    *    <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared.
947    *    <br>The tags that are not declared may be organized randomly or based on the tools' logic.
948    *    <br>Each tag name in the list MUST be unique.
949    *    <br>Ignored if <jk>null</jk>.
950    * @return This object.
951    */
952   public Swagger addTags(Collection<Tag> values) {
953      tags = setBuilder(tags).sparse().addAll(values).build();
954      return this;
955   }
956
957   //-----------------------------------------------------------------------------------------------------------------
958   // Convenience methods
959   //-----------------------------------------------------------------------------------------------------------------
960
961   /**
962    * Shortcut for calling <c>getPaths().get(path);</c>
963    *
964    * @param path The path (e.g. <js>"/foo"</js>).  Must not be <jk>null</jk>.
965    * @return The operation map for the specified path, or <jk>null</jk> if it doesn't exist.
966    */
967   public OperationMap getPath(String path) {
968      assertArgNotNull("path", path);
969      return opt(getPaths()).map(x -> x.get(path)).orElse(null);
970   }
971
972   /**
973    * Shortcut for calling <c>getPaths().get(path).get(operation);</c>
974    *
975    * @param path The path (e.g. <js>"/foo"</js>).  Must not be <jk>null</jk>.
976    * @param operation The HTTP operation (e.g. <js>"get"</js>).  Must not be <jk>null</jk>.
977    * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist.
978    */
979   public Operation getOperation(String path, String operation) {
980      assertArgNotNull("path", path);
981      assertArgNotNull("operation", operation);
982      return opt(getPath(path)).map(x -> x.get(operation)).orElse(null);
983   }
984
985   /**
986    * Shortcut for calling <c>getPaths().get(path).get(operation).getResponse(status);</c>
987    *
988    * @param path The path (e.g. <js>"/foo"</js>).  Must not be <jk>null</jk>.
989    * @param operation The HTTP operation (e.g. <js>"get"</js>).  Must not be <jk>null</jk>.
990    * @param status The HTTP response status (e.g. <js>"200"</js>).  Must not be <jk>null</jk>.
991    * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist.
992    */
993   public ResponseInfo getResponseInfo(String path, String operation, String status) {
994      assertArgNotNull("path", path);
995      assertArgNotNull("operation", operation);
996      assertArgNotNull("status", status);
997      return opt(getPath(path)).map(x -> x.get(operation)).map(x -> x.getResponse(status)).orElse(null);
998   }
999
1000   /**
1001    * Shortcut for calling <c>getPaths().get(path).get(operation).getResponse(status);</c>
1002    *
1003    * @param path The path (e.g. <js>"/foo"</js>).
1004    * @param operation The HTTP operation (e.g. <js>"get"</js>).
1005    * @param status The HTTP response status (e.g. <js>"200"</js>).
1006    * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist.
1007    */
1008   public ResponseInfo getResponseInfo(String path, String operation, int status) {
1009      return getResponseInfo(path, operation, String.valueOf(status));
1010   }
1011
1012   /**
1013    * Convenience method for calling <c>getPath(path).get(method).getParameter(in,name);</c>
1014    *
1015    * @param path The HTTP path.  Must not be <jk>null</jk>.
1016    * @param method The HTTP method.  Must not be <jk>null</jk>.
1017    * @param in The parameter type.  Must not be <jk>null</jk>.
1018    * @param name The parameter name.  Can be <jk>null</jk> for parameter type <c>body</c>.
1019    * @return The parameter information or <jk>null</jk> if not found.
1020    */
1021   public ParameterInfo getParameterInfo(String path, String method, String in, String name) {
1022      assertArgNotNull("path", path);
1023      assertArgNotNull("method", method);
1024      assertArgNotNull("in", in);
1025      return opt(getPath(path)).map(x -> x.get(method)).map(x -> x.getParameter(in, name)).orElse(null);
1026   }
1027
1028   @Override /* Overridden from SwaggerElement */
1029   public <T> T get(String property, Class<T> type) {
1030      assertArgNotNull("property", property);
1031      return switch (property) {
1032         case "basePath" -> toType(getBasePath(), type);
1033         case "consumes" -> toType(getConsumes(), type);
1034         case "definitions" -> toType(getDefinitions(), type);
1035         case "externalDocs" -> toType(getExternalDocs(), type);
1036         case "host" -> toType(getHost(), type);
1037         case "info" -> toType(getInfo(), type);
1038         case "parameters" -> toType(getParameters(), type);
1039         case "paths" -> toType(getPaths(), type);
1040         case "produces" -> toType(getProduces(), type);
1041         case "responses" -> toType(getResponses(), type);
1042         case "schemes" -> toType(getSchemes(), type);
1043         case "security" -> toType(getSecurity(), type);
1044         case "securityDefinitions" -> toType(getSecurityDefinitions(), type);
1045         case "swagger" -> toType(getSwagger(), type);
1046         case "tags" -> toType(getTags(), type);
1047         default -> super.get(property, type);
1048      };
1049   }
1050
1051   @SuppressWarnings("rawtypes")
1052   @Override /* Overridden from SwaggerElement */
1053   public Swagger set(String property, Object value) {
1054      assertArgNotNull("property", property);
1055      return switch (property) {
1056         case "basePath" -> setBasePath(Utils.s(value));
1057         case "consumes" -> setConsumes(listBuilder(MediaType.class).sparse().addAny(value).build());
1058         case "definitions" -> setDefinitions(mapBuilder(String.class,JsonMap.class).sparse().addAny(value).build());
1059         case "externalDocs" -> setExternalDocs(toType(value, ExternalDocumentation.class));
1060         case "host" -> setHost(Utils.s(value));
1061         case "info" -> setInfo(toType(value, Info.class));
1062         case "parameters" -> setParameters(mapBuilder(String.class,ParameterInfo.class).sparse().addAny(value).build());
1063         case "paths" -> setPaths(mapBuilder(String.class,OperationMap.class).sparse().addAny(value).build());
1064         case "produces" -> setProduces(listBuilder(MediaType.class).sparse().addAny(value).build());
1065         case "responses" -> setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build());
1066         case "schemes" -> setSchemes(listBuilder(String.class).sparse().addAny(value).build());
1067         case "security" -> setSecurity((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build());
1068         case "securityDefinitions" -> setSecurityDefinitions(mapBuilder(String.class,SecurityScheme.class).sparse().addAny(value).build());
1069         case "swagger" -> setSwagger(Utils.s(value));
1070         case "tags" -> setTags(listBuilder(Tag.class).sparse().addAny(value).build());
1071         default -> {
1072            super.set(property, value);
1073            yield this;
1074         }
1075      };
1076   }
1077
1078   @Override /* Overridden from SwaggerElement */
1079   public Set<String> keySet() {
1080      var s = setBuilder(String.class)
1081         .addIf(basePath != null, "basePath")
1082         .addIf(consumes != null, "consumes")
1083         .addIf(definitions != null, "definitions")
1084         .addIf(externalDocs != null, "externalDocs")
1085         .addIf(host != null, "host")
1086         .addIf(info != null, "info")
1087         .addIf(parameters != null, "parameters")
1088         .addIf(paths != null, "paths")
1089         .addIf(produces != null, "produces")
1090         .addIf(responses != null, "responses")
1091         .addIf(schemes != null, "schemes")
1092         .addIf(security != null, "security")
1093         .addIf(securityDefinitions != null, "securityDefinitions")
1094         .addIf(swagger != null, "swagger")
1095         .addIf(tags != null, "tags")
1096         .build();
1097      return new MultiSet<>(s, super.keySet());
1098   }
1099
1100   /**
1101    * A synonym of {@link #toString()}.
1102    * @return This object serialized as JSON.
1103    */
1104   public String asJson() {
1105      return toString();
1106   }
1107
1108   @Override /* Object */
1109   public String toString() {
1110      return JsonSerializer.DEFAULT.toString(this);
1111   }
1112
1113   /**
1114    * Resolves a <js>"$ref"</js> tags to nodes in this swagger document.
1115    *
1116    * @param <T> The class to convert the reference to.
1117    * @param ref The ref tag value.  Must not be <jk>null</jk> or blank.
1118    * @param c The class to convert the reference to.  Must not be <jk>null</jk>.
1119    * @return The referenced node, or <jk>null</jk> if not found.
1120    */
1121   public <T> T findRef(String ref, Class<T> c) {
1122      assertArgNotNullOrBlank("ref", ref);
1123      assertArgNotNull("c", c);
1124      if (! ref.startsWith("#/"))
1125         throw new BasicRuntimeException("Unsupported reference:  ''{0}''", ref);
1126      try {
1127         return new ObjectRest(this).get(ref.substring(1), c);
1128      } catch (Exception e) {
1129         throw new BeanRuntimeException(e, c, "Reference ''{0}'' could not be converted to type ''{1}''.", ref, className(c));
1130      }
1131   }
1132
1133   /**
1134    * Sets strict mode on this bean.
1135    *
1136    * @return This object.
1137    */
1138   @Override
1139   public Swagger strict() {
1140      super.strict();
1141      return this;
1142   }
1143
1144   /**
1145    * Sets strict mode on this bean.
1146    *
1147    * @param value
1148    *    The new value for this property.
1149    *    <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
1150    *    <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>).
1151    * @return This object.
1152    */
1153   @Override
1154   public Swagger strict(Object value) {
1155      super.strict(value);
1156      return this;
1157   }
1158
1159}