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