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