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.CollectionUtils.*;
017import static org.apache.juneau.internal.ConverterUtils.*;
018
019import java.util.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.annotation.*;
023import org.apache.juneau.internal.*;
024
025/**
026 * Describes a single API operation on a path.
027 *
028 * <h5 class='section'>Example:</h5>
029 * <p class='bjava'>
030 *    <jc>// Construct using SwaggerBuilder.</jc>
031 *    Operation <jv>operation</jv> = <jsm>operation</jsm>()
032 *       .tags(<js>"pet"</js>)
033 *       .summary(<js>"Updates a pet in the store with form data"</js>)
034 *       .description(<js>""</js>)
035 *       .operationId(<js>"updatePetWithForm"</js>)
036 *       .consumes(<js>"application/x-www-form-urlencoded"</js>)
037 *       .produces(<js>"application/json"</js>, <js>"application/xml"</js>)
038 *       .parameters(
039 *          <jsm>parameter</jsm>()
040 *             .name(<js>"petId"</js>)
041 *             .in(<js>"path"</js>)
042 *             .description(<js>"ID of pet that needs to be updated"</js>)
043 *             .required(<jk>true</jk>)
044 *             .type(<js>"string"</js>),
045 *          <jsm>parameter</jsm>()
046 *             .name(<js>"name"</js>)
047 *             .in(<js>"formData"</js>)
048 *             .description(<js>"Updated name of the pet"</js>)
049 *             .required(<jk>false</jk>)
050 *             .type(<js>"string"</js>),
051 *          <jsm>parameter</jsm>()
052 *             .name(<js>"status"</js>)
053 *             .in(<js>"formData"</js>)
054 *             .description(<js>"Updated status of the pet"</js>)
055 *             .required(<jk>false</jk>)
056 *             .type(<js>"string"</js>)
057 *       )
058 *       .response(200, <jsm>responseInfo</jsm>(<js>"Pet updated."</js>))
059 *       .response(405, <jsm>responseInfo</jsm>(<js>"Invalid input."</js>))
060 *       .security(<js>"petstore_auth"</js>, <js>"write:pets"</js>, <js>"read:pets"</js>);
061 *
062 *    <jc>// Serialize using JsonSerializer.</jc>
063 *    String <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.toString(<jv>operation</jv>);
064 *
065 *    <jc>// Or just use toString() which does the same as above.</jc>
066 *    <jv>json</jv> = <jv>operation</jv>.toString();
067 * </p>
068 * <p class='bjson'>
069 *    <jc>// Output</jc>
070 *    {
071 *       <js>"tags"</js>: [
072 *          <js>"pet"</js>
073 *       ],
074 *       <js>"summary"</js>: <js>"Updates a pet in the store with form data"</js>,
075 *       <js>"description"</js>: <js>""</js>,
076 *       <js>"operationId"</js>: <js>"updatePetWithForm"</js>,
077 *       <js>"consumes"</js>: [
078 *          <js>"application/x-www-form-urlencoded"</js>
079 *       ],
080 *       <js>"produces"</js>: [
081 *          <js>"application/json"</js>,
082 *          <js>"application/xml"</js>
083 *       ],
084 *       <js>"parameters"</js>: [
085 *          {
086 *             <js>"name"</js>: <js>"petId"</js>,
087 *             <js>"in"</js>: <js>"path"</js>,
088 *             <js>"description"</js>: <js>"ID of pet that needs to be updated"</js>,
089 *             <js>"required"</js>: <jk>true</jk>,
090 *             <js>"type"</js>: <js>"string"</js>
091 *          },
092 *          {
093 *             <js>"name"</js>: <js>"name"</js>,
094 *             <js>"in"</js>: <js>"formData"</js>,
095 *             <js>"description"</js>: <js>"Updated name of the pet"</js>,
096 *             <js>"required"</js>: <jk>false</jk>,
097 *             <js>"type"</js>: <js>"string"</js>
098 *          },
099 *          {
100 *             <js>"name"</js>: <js>"status"</js>,
101 *             <js>"in"</js>: <js>"formData"</js>,
102 *             <js>"description"</js>: <js>"Updated status of the pet"</js>,
103 *             <js>"required"</js>: <jk>false</jk>,
104 *             <js>"type"</js>: <js>"string"</js>
105 *          }
106 *       ],
107 *       <js>"responses"</js>: {
108 *          <js>"200"</js>: {
109 *             <js>"description"</js>: <js>"Pet updated."</js>
110 *          },
111 *          <js>"405"</js>: {
112 *             <js>"description"</js>: <js>"Invalid input"</js>
113 *          }
114 *       },
115 *       <js>"security"</js>: [
116 *          {
117 *             <js>"petstore_auth"</js>: [
118 *                <js>"write:pets"</js>,
119 *                <js>"read:pets"</js>
120 *             ]
121 *          }
122 *       ]
123 *    }
124 * </p>
125 *
126 * <h5 class='section'>See Also:</h5><ul>
127 *    <li class='link'><a class="doclink" href="../../../../../index.html#jrs.Swagger">Overview &gt; juneau-rest-server &gt; Swagger</a>
128 * </ul>
129 */
130@Bean(properties="operationId,summary,description,tags,externalDocs,consumes,produces,parameters,responses,schemes,deprecated,security,*")
131@FluentSetters
132public class Operation extends SwaggerElement {
133
134   private String
135      summary,
136      description,
137      operationId;
138   private Boolean deprecated;
139   private ExternalDocumentation externalDocs;
140   private Set<String>
141      tags,
142      schemes;
143   private Set<MediaType>
144      consumes,
145      produces;
146   private List<ParameterInfo> parameters;
147   private List<Map<String,List<String>>> security;
148   private Map<String,ResponseInfo> responses;
149
150   /**
151    * Default constructor.
152    */
153   public Operation() {}
154
155   /**
156    * Copy constructor.
157    *
158    * @param copyFrom The object to copy.
159    */
160   public Operation(Operation copyFrom) {
161      super(copyFrom);
162
163      this.consumes = copyOf(copyFrom.consumes);
164      this.deprecated = copyFrom.deprecated;
165      this.description = copyFrom.description;
166      this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy();
167      this.operationId = copyFrom.operationId;
168      this.produces = copyOf(copyFrom.produces);
169      this.schemes = copyOf(copyFrom.schemes);
170      this.summary = copyFrom.summary;
171      this.tags = copyOf(copyFrom.tags);
172
173      if (copyFrom.parameters == null) {
174         this.parameters = null;
175      } else {
176         this.parameters = list();
177         copyFrom.parameters.forEach(x -> this.parameters.add(x.copy()));
178      }
179
180      if (copyFrom.responses == null) {
181         this.responses = null;
182      } else {
183         this.responses = map();
184         copyFrom.responses.forEach((k,v) -> this.responses.put(k, v.copy()));
185      }
186
187      if (copyFrom.security == null) {
188         this.security = null;
189      } else {
190         this.security = list();
191         copyFrom.security.forEach(x -> {
192            Map<String,List<String>> m2 = map();
193            x.forEach((k,v) -> m2.put(k, copyOf(v)));
194            this.security.add(m2);
195         });
196      }
197   }
198
199   /**
200    * Make a deep copy of this object.
201    *
202    * @return A deep copy of this object.
203    */
204   public Operation copy() {
205      return new Operation(this);
206   }
207
208   //-----------------------------------------------------------------------------------------------------------------
209   // Properties
210   //-----------------------------------------------------------------------------------------------------------------
211
212   /**
213    * Bean property getter:  <property>consumes</property>.
214    *
215    * <p>
216    * A list of MIME types the operation can consume.
217    *
218    * @return The property value, or <jk>null</jk> if it is not set.
219    */
220   public Set<MediaType> getConsumes() {
221      return consumes;
222   }
223
224   /**
225    * Bean property setter:  <property>consumes</property>.
226    *
227    * <p>
228    * A list of MIME types the operation can consume.
229    *
230    * @param value
231    *    The new value for this property.
232    *    <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
233    *    <br>Can be <jk>null</jk> to unset the property.
234    * @return This object.
235    */
236   public Operation setConsumes(Collection<MediaType> value) {
237      consumes = setFrom(value);
238      return this;
239   }
240
241   /**
242    * Bean property setter:  <property>consumes</property>.
243    *
244    * <p>
245    * A list of MIME types the operation can consume.
246    *
247    * @param value
248    *    The new value for this property.
249    *    <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
250    * @return This object.
251    */
252   public Operation setConsumes(MediaType...value) {
253      return setConsumes(Arrays.asList(value));
254   }
255
256   /**
257    * Bean property fluent setter:  <property>consumes</property>.
258    *
259    * <p>
260    * A list of MIME types the operation can consume.
261    *
262    * @param value
263    *    The new value for this property.
264    * @return This object.
265    */
266   public Operation addConsumes(MediaType...value) {
267      setConsumes(setBuilder(MediaType.class).sparse().add(value).build());
268      return this;
269   }
270
271   /**
272    * Bean property getter:  <property>deprecated</property>.
273    *
274    * <p>
275    * Declares this operation to be deprecated.
276    *
277    * @return The property value, or <jk>null</jk> if it is not set.
278    */
279   public Boolean getDeprecated() {
280      return deprecated;
281   }
282
283   /**
284    * Bean property getter:  <property>deprecated</property>.
285    *
286    * <p>
287    * Declares this operation to be deprecated.
288    *
289    * @return The property value, or <jk>false</jk> if it is not set.
290    */
291   public boolean isDeprecated() {
292      return deprecated != null && deprecated == true;
293   }
294
295   /**
296    * Bean property setter:  <property>deprecated</property>.
297    *
298    * <p>
299    * Declares this operation to be deprecated.
300    *
301    * @param value The new value for this property.
302    * @return This object.
303    */
304   public Operation setDeprecated(Boolean value) {
305      deprecated = value;
306      return this;
307   }
308
309   /**
310    * Bean property getter:  <property>description</property>.
311    *
312    * <p>
313    * A verbose explanation of the operation behavior.
314    *
315    * @return The property value, or <jk>null</jk> if it is not set.
316    */
317   public String getDescription() {
318      return description;
319   }
320
321   /**
322    * Bean property setter:  <property>description</property>.
323    *
324    * <p>
325    * A verbose explanation of the operation behavior.
326    *
327    * @param value
328    *    The new value for this property.
329    *    <br><a class="doclink" href="https://help.github.com/articles/github-flavored-markdown">GFM syntax</a> can be used for rich text representation.
330    *    <br>Can be <jk>null</jk> to unset the property.
331    * @return This object.
332    */
333   public Operation setDescription(String value) {
334      description = value;
335      return this;
336   }
337
338   /**
339    * Bean property getter:  <property>externalDocs</property>.
340    *
341    * <p>
342    * Additional external documentation for this operation.
343    *
344    * @return The property value, or <jk>null</jk> if it is not set.
345    */
346   public ExternalDocumentation getExternalDocs() {
347      return externalDocs;
348   }
349
350   /**
351    * Bean property setter:  <property>externalDocs</property>.
352    *
353    * <p>
354    * Additional external documentation for this operation.
355    *
356    * @param value
357    *    The values to add to this property.
358    *    <br>Can be <jk>null</jk> to unset the property.
359    * @return This object.
360    */
361   public Operation setExternalDocs(ExternalDocumentation value) {
362      externalDocs = value;
363      return this;
364   }
365
366   /**
367    * Bean property getter:  <property>operationId</property>.
368    *
369    * <p>
370    * Unique string used to identify the operation.
371    *
372    * @return The property value, or <jk>null</jk> if it is not set.
373    */
374   public String getOperationId() {
375      return operationId;
376   }
377
378   /**
379    * Bean property setter:  <property>operationId</property>.
380    *
381    * <p>
382    * Unique string used to identify the operation.
383    *
384    * @param value
385    *    The new value for this property.
386    *    <br>The id MUST be unique among all operations described in the API.
387    *    <br>Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is recommended to
388    *    follow common programming naming conventions.
389    *    <br>Can be <jk>null</jk> to unset the property.
390    * @return This object.
391    */
392   public Operation setOperationId(String value) {
393      operationId = value;
394      return this;
395   }
396
397   /**
398    * Bean property getter:  <property>parameters</property>.
399    *
400    * <p>
401    * A list of parameters that are applicable for this operation.
402    *
403    * <h5 class='section'>Notes:</h5><ul>
404    *    <li class='note'>
405    *       If a parameter is already defined at the <a class="doclink" href="https://swagger.io/specification#pathItemObject">Path Item</a>,
406    *       the new definition will override it, but can never remove it.
407    *    <li class='note'>
408    *       The list MUST NOT include duplicated parameters.
409    *    <li class='note'>
410    *       A unique parameter is defined by a combination of a <c>name</c> and <c>location</c>.
411    *    <li class='note'>
412    *       The list can use the <a class="doclink" href="https://swagger.io/specification#referenceObject">Swagger Reference Object</a>
413    *       to link to parameters that are defined at the <a class='doclink' href='https://swagger.io/specification/v2#parameterObject'>Swagger Object's parameters</a>.
414    *    <li class='note'>
415    *       There can be one <js>"body"</js> parameter at most.
416    * </ul>
417    *
418    * @return The property value, or <jk>null</jk> if it is not set.
419    */
420   public List<ParameterInfo> getParameters() {
421      return parameters;
422   }
423
424   /**
425    * Returns the parameter with the specified type and name.
426    *
427    * @param in The parameter in.
428    * @param name The parameter name.  Can be <jk>null</jk> for parameter type <c>body</c>.
429    * @return The matching parameter info, or <jk>null</jk> if not found.
430    */
431   public ParameterInfo getParameter(String in, String name) {
432      if (parameters != null)
433         for (ParameterInfo pi : parameters)
434            if (eq(pi.getIn(), in))
435               if (eq(pi.getName(), name) || "body".equals(pi.getIn()))
436                  return pi;
437      return null;
438   }
439
440   /**
441    * Bean property setter:  <property>parameters</property>.
442    *
443    * <p>
444    * A list of parameters that are applicable for this operation.
445    *
446    * @param value
447    *    The new value for this property.
448    *    <br>Can be <jk>null</jk> to unset the property.
449    * @return This object.
450    */
451   public Operation setParameters(Collection<ParameterInfo> value) {
452      parameters = listFrom(value);
453      return this;
454   }
455
456   /**
457    * Bean property setter:  <property>parameters</property>.
458    *
459    * <p>
460    * A list of parameters that are applicable for this operation.
461    *
462    * @param value
463    *    The new value for this property.
464    * @return This object.
465    */
466   public Operation setParameters(ParameterInfo...value) {
467      return setParameters(Arrays.asList(value));
468   }
469
470   /**
471    * Bean property fluent setter:  <property>parameters</property>.
472    *
473    * <p>
474    * A list of parameters that are applicable for this operation.
475    *
476    * @param value
477    *    The new value for this property.
478    * @return This object.
479    */
480   public Operation addParameters(ParameterInfo...value) {
481      setParameters(listBuilder(ParameterInfo.class).sparse().add(value).build());
482      return this;
483   }
484
485   //-----------------------------------------------------------------------------------------------------------------
486   // produces
487   //-----------------------------------------------------------------------------------------------------------------
488
489   /**
490    * Bean property getter:  <property>produces</property>.
491    *
492    * <p>
493    * A list of MIME types the operation can produce.
494    *
495    * @return The property value, or <jk>null</jk> if it is not set.
496    */
497   public Set<MediaType> getProduces() {
498      return produces;
499   }
500
501   /**
502    * Bean property setter:  <property>produces</property>.
503    *
504    * <p>
505    * A list of MIME types the operation can produce.
506    *
507    * @param value
508    *    The new value for this property.
509    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
510    *    <br>Can be <jk>null</jk> to unset the property.
511    * @return This object.
512    */
513   public Operation setProduces(Collection<MediaType> value) {
514      produces = setFrom(value);
515      return this;
516   }
517
518   /**
519    * Bean property setter:  <property>produces</property>.
520    *
521    * <p>
522    * A list of MIME types the operation can produce.
523    *
524    * @param value
525    *    The new value for this property.
526    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
527    * @return This object.
528    */
529   public Operation setProduces(MediaType...value) {
530      return setProduces(Arrays.asList(value));
531   }
532
533   /**
534    * Bean property fluent setter:  <property>produces</property>.
535    *
536    * <p>
537    * A list of MIME types the operation can produce.
538    *
539    * @param value
540    *    The new value for this property.
541    * @return This object.
542    */
543   public Operation addProduces(MediaType...value) {
544      setProduces(setBuilder(MediaType.class).sparse().add(value).build());
545      return this;
546   }
547
548   /**
549    * Bean property getter:  <property>responses</property>.
550    *
551    * <p>
552    * The list of possible responses as they are returned from executing this operation.
553    *
554    * @return The property value, or <jk>null</jk> if it is not set.
555    */
556   public Map<String,ResponseInfo> getResponses() {
557      return responses;
558   }
559
560   /**
561    * Returns the response info with the given status code.
562    *
563    * @param status The HTTP status code.
564    * @return The response info, or <jk>null</jk> if not found.
565    */
566   public ResponseInfo getResponse(String status) {
567      if (responses != null)
568         return responses.get(status);
569      return null;
570   }
571
572   /**
573    * Returns the response info with the given status code.
574    *
575    * @param status The HTTP status code.
576    * @return The response info, or <jk>null</jk> if not found.
577    */
578   public ResponseInfo getResponse(int status) {
579      return getResponse(String.valueOf(status));
580   }
581
582   /**
583    * Bean property setter:  <property>responses</property>.
584    *
585    * <p>
586    * The list of possible responses as they are returned from executing this operation.
587    *
588    * @param value
589    *    The new value for this property.
590    *    <br>Property value is required.
591    * @return This object.
592    */
593   public Operation setResponses(Map<String,ResponseInfo> value) {
594      responses = copyOf(value);
595      return this;
596   }
597
598   /**
599    * Adds a single value to the <property>responses</property> property.
600    *
601    * @param statusCode The HTTP status code.
602    * @param response The response description.
603    * @return This object.
604    */
605   public Operation addResponse(String statusCode, ResponseInfo response) {
606      responses = mapBuilder(responses).add(statusCode, response).build();
607      return this;
608   }
609
610   /**
611    * Bean property getter:  <property>schemes</property>.
612    *
613    * <p>
614    * The transfer protocol for the operation.
615    *
616    * @return The property value, or <jk>null</jk> if it is not set.
617    */
618   public Set<String> getSchemes() {
619      return schemes;
620   }
621
622   /**
623    * Bean property setter:  <property>schemes</property>.
624    *
625    * <p>
626    * The transfer protocol for the operation.
627    *
628    * @param value
629    *    The new value for this property.
630    *    <br>Valid values:
631    *    <ul>
632    *       <li><js>"http"</js>
633    *       <li><js>"https"</js>
634    *       <li><js>"ws"</js>
635    *       <li><js>"wss"</js>
636    *    </ul>
637    *    <br>Can be <jk>null</jk> to unset the property.
638    * @return This object.
639    */
640   public Operation setSchemes(Collection<String> value) {
641      schemes = setFrom(value);
642      return this;
643   }
644
645   /**
646    * Bean property fluent setter:  <property>schemes</property>.
647    *
648    * <p>
649    * The transfer protocol for the operation.
650    *
651    * @param value
652    *    The new value for this property.
653    *    <br>String values can also be JSON arrays.
654    * @return This object.
655    */
656   public Operation addSchemes(String...value) {
657      setSchemes(setBuilder(String.class).sparse().addJson(value).build());
658      return this;
659   }
660
661   /**
662    * Bean property getter:  <property>security</property>.
663    *
664    * <p>
665    * A declaration of which security schemes are applied for this operation.
666    *
667    * @return The property value, or <jk>null</jk> if it is not set.
668    */
669   public List<Map<String,List<String>>> getSecurity() {
670      return security;
671   }
672
673   /**
674    * Bean property setter:  <property>security</property>.
675    *
676    * <p>
677    * A declaration of which security schemes are applied for this operation.
678    *
679    * @param value
680    *    The new value for this property.
681    *    <br>Can be <jk>null</jk> to unset the property.
682    * @return This object.
683    */
684   public Operation setSecurity(Collection<Map<String,List<String>>> value) {
685      security = listFrom(value);
686      return this;
687   }
688
689   /**
690    * Same as {@link #addSecurity(String, String...)}.
691    *
692    * @param scheme
693    *    The scheme name.
694    * @param alternatives
695    *    The list of values describes alternative security schemes that can be used (that is, there is a logical OR
696    *    between the security requirements).
697    * @return This object.
698    */
699   public Operation addSecurity(String scheme, String...alternatives) {
700      Map<String,List<String>> m = map();
701      m.put(scheme, alist(alternatives));
702      security = listBuilder(security).add(m).build();
703      return this;
704   }
705
706   /**
707    * Bean property getter:  <property>summary</property>.
708    *
709    * <p>
710    * A short summary of what the operation does.
711    *
712    * @return The property value, or <jk>null</jk> if it is not set.
713    */
714   public String getSummary() {
715      return summary;
716   }
717
718   /**
719    * Bean property setter:  <property>summary</property>.
720    *
721    * <p>
722    * A short summary of what the operation does.
723    *
724    * @param value
725    *    The new value for this property.
726    *    <br>Can be <jk>null</jk> to unset the property.
727    * @return This object.
728    */
729   public Operation setSummary(String value) {
730      summary = value;
731      return this;
732   }
733
734   /**
735    * Bean property getter:  <property>tags</property>.
736    *
737    * <p>
738    * A list of tags for API documentation control.
739    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
740    *
741    * @return The property value, or <jk>null</jk> if it is not set.
742    */
743   public Set<String> getTags() {
744      return tags;
745   }
746
747   /**
748    * Bean property setter:  <property>tags</property>.
749    *
750    * <p>
751    * A list of tags for API documentation control.
752    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
753    *
754    * @param value
755    *    The new value for this property.
756    *    <br>Can be <jk>null</jk> to unset the property.
757    * @return This object.
758    */
759   public Operation setTags(Collection<String> value) {
760      tags = setFrom(value);
761      return this;
762   }
763
764   /**
765    * Bean property fluent setter:  <property>tags</property>.
766    *
767    * <p>
768    * A list of tags for API documentation control.
769    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
770    *
771    * @param value
772    *    The new value for this property.
773    * @return This object.
774    */
775   public Operation setTags(String...value) {
776      setTags(setBuilder(String.class).sparse().add(value).build());
777      return this;
778   }
779
780   /**
781    * Bean property fluent adder:  <property>tags</property>.
782    *
783    * <p>
784    * A list of tags for API documentation control.
785    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
786    *
787    * @param value
788    *    The values to add to this property.
789    * @return This object.
790    */
791   public Operation addTags(String...value) {
792      setTags(setBuilder(tags).sparse().add(value).build());
793      return this;
794   }
795
796   // <FluentSetters>
797
798   // </FluentSetters>
799
800   @Override /* SwaggerElement */
801   public <T> T get(String property, Class<T> type) {
802      if (property == null)
803         return null;
804      switch (property) {
805         case "consumes": return toType(getConsumes(), type);
806         case "deprecated": return toType(getDeprecated(), type);
807         case "description": return toType(getDescription(), type);
808         case "externalDocs": return toType(getExternalDocs(), type);
809         case "operationId": return toType(getOperationId(), type);
810         case "parameters": return toType(getParameters(), type);
811         case "produces": return toType(getProduces(), type);
812         case "responses": return toType(getResponses(), type);
813         case "schemes": return toType(getSchemes(), type);
814         case "security": return toType(getSecurity(), type);
815         case "summary": return toType(getSummary(), type);
816         case "tags": return toType(getTags(), type);
817         default: return super.get(property, type);
818      }
819   }
820
821   @SuppressWarnings({"unchecked","rawtypes"})
822   @Override /* SwaggerElement */
823   public Operation set(String property, Object value) {
824      if (property == null)
825         return this;
826      switch (property) {
827         case "consumes": return setConsumes(listBuilder(MediaType.class).sparse().addAny(value).build());
828         case "deprecated": return setDeprecated(toBoolean(value));
829         case "description": return setDescription(stringify(value));
830         case "externalDocs": return setExternalDocs(toType(value, ExternalDocumentation.class));
831         case "operationId": return setOperationId(stringify(value));
832         case "parameters": return setParameters(listBuilder(ParameterInfo.class).sparse().addAny(value).build());
833         case "produces": return setProduces(listBuilder(MediaType.class).sparse().addAny(value).build());
834         case "responses": return setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build());
835         case "schemes": return setSchemes(listBuilder(String.class).sparse().addAny(value).build());
836         case "security": return setSecurity((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build());
837         case "summary": return setSummary(stringify(value));
838         case "tags": return setTags(listBuilder(String.class).sparse().addAny(value).build());
839         default:
840            super.set(property, value);
841            return this;
842      }
843   }
844
845   @Override /* SwaggerElement */
846   public Set<String> keySet() {
847      Set<String> s = setBuilder(String.class)
848         .addIf(consumes != null, "consumes")
849         .addIf(deprecated != null, "deprecated")
850         .addIf(description != null, "description")
851         .addIf(externalDocs != null, "externalDocs")
852         .addIf(operationId != null, "operationId")
853         .addIf(parameters != null, "parameters")
854         .addIf(produces != null, "produces")
855         .addIf(responses != null, "responses")
856         .addIf(schemes != null, "schemes")
857         .addIf(security != null, "security")
858         .addIf(summary != null, "summary")
859         .addIf(tags != null, "tags")
860         .build();
861      return new MultiSet<>(s, super.keySet());
862   }
863}