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