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.internal.StringUtils.*;
016import static org.apache.juneau.internal.ObjectUtils.*;
017import static org.apache.juneau.internal.CollectionUtils.*;
018
019import java.util.*;
020
021import org.apache.juneau.annotation.*;
022import org.apache.juneau.http.*;
023import org.apache.juneau.internal.*;
024import org.apache.juneau.utils.*;
025
026/**
027 * Describes a single API operation on a path.
028 *
029 * <h5 class='section'>Example:</h5>
030 * <p class='bcode w800'>
031 *    <jc>// Construct using SwaggerBuilder.</jc>
032 *    Operation x = <jsm>operation</jsm>()
033 *       .tags(<js>"pet"</js>)
034 *       .summary(<js>"Updates a pet in the store with form data"</js>)
035 *       .description(<js>""</js>)
036 *       .operationId(<js>"updatePetWithForm"</js>)
037 *       .consumes(<js>"application/x-www-form-urlencoded"</js>)
038 *       .produces(<js>"application/json"</js>, <js>"application/xml"</js>)
039 *       .parameters(
040 *          <jsm>parameter</jsm>()
041 *             .name(<js>"petId"</js>)
042 *             .in(<js>"path"</js>)
043 *             .description(<js>"ID of pet that needs to be updated"</js>)
044 *             .required(<jk>true</jk>)
045 *             .type(<js>"string"</js>),
046 *          <jsm>parameter</jsm>()
047 *             .name(<js>"name"</js>)
048 *             .in(<js>"formData"</js>)
049 *             .description(<js>"Updated name of the pet"</js>)
050 *             .required(<jk>false</jk>)
051 *             .type(<js>"string"</js>),
052 *          <jsm>parameter</jsm>()
053 *             .name(<js>"status"</js>)
054 *             .in(<js>"formData"</js>)
055 *             .description(<js>"Updated status of the pet"</js>)
056 *             .required(<jk>false</jk>)
057 *             .type(<js>"string"</js>)
058 *       )
059 *       .response(200, <jsm>responseInfo</jsm>(<js>"Pet updated."</js>))
060 *       .response(405, <jsm>responseInfo</jsm>(<js>"Invalid input."</js>))
061 *       .security(<js>"petstore_auth"</js>, <js>"write:pets"</js>, <js>"read:pets"</js>);
062 *
063 *    <jc>// Serialize using JsonSerializer.</jc>
064 *    String json = JsonSerializer.<jsf>DEFAULT</jsf>.toString(x);
065 *
066 *    <jc>// Or just use toString() which does the same as above.</jc>
067 *    String json = x.toString();
068 * </p>
069 * <p class='bcode w800'>
070 *    <jc>// Output</jc>
071 *    {
072 *       <js>"tags"</js>: [
073 *          <js>"pet"</js>
074 *       ],
075 *       <js>"summary"</js>: <js>"Updates a pet in the store with form data"</js>,
076 *       <js>"description"</js>: <js>""</js>,
077 *       <js>"operationId"</js>: <js>"updatePetWithForm"</js>,
078 *       <js>"consumes"</js>: [
079 *          <js>"application/x-www-form-urlencoded"</js>
080 *       ],
081 *       <js>"produces"</js>: [
082 *          <js>"application/json"</js>,
083 *          <js>"application/xml"</js>
084 *       ],
085 *       <js>"parameters"</js>: [
086 *          {
087 *             <js>"name"</js>: <js>"petId"</js>,
088 *             <js>"in"</js>: <js>"path"</js>,
089 *             <js>"description"</js>: <js>"ID of pet that needs to be updated"</js>,
090 *             <js>"required"</js>: <jk>true</jk>,
091 *             <js>"type"</js>: <js>"string"</js>
092 *          },
093 *          {
094 *             <js>"name"</js>: <js>"name"</js>,
095 *             <js>"in"</js>: <js>"formData"</js>,
096 *             <js>"description"</js>: <js>"Updated name of the pet"</js>,
097 *             <js>"required"</js>: <jk>false</jk>,
098 *             <js>"type"</js>: <js>"string"</js>
099 *          },
100 *          {
101 *             <js>"name"</js>: <js>"status"</js>,
102 *             <js>"in"</js>: <js>"formData"</js>,
103 *             <js>"description"</js>: <js>"Updated status of the pet"</js>,
104 *             <js>"required"</js>: <jk>false</jk>,
105 *             <js>"type"</js>: <js>"string"</js>
106 *          }
107 *       ],
108 *       <js>"responses"</js>: {
109 *          <js>"200"</js>: {
110 *             <js>"description"</js>: <js>"Pet updated."</js>
111 *          },
112 *          <js>"405"</js>: {
113 *             <js>"description"</js>: <js>"Invalid input"</js>
114 *          }
115 *       },
116 *       <js>"security"</js>: [
117 *          {
118 *             <js>"petstore_auth"</js>: [
119 *                <js>"write:pets"</js>,
120 *                <js>"read:pets"</js>
121 *             ]
122 *          }
123 *       ]
124 *    }
125 * </p>
126 *
127 * <ul class='seealso'>
128 *    <li class='link'>{@doc juneau-dto.Swagger}
129 * </ul>
130 */
131@Bean(properties="operationId,summary,description,tags,externalDocs,consumes,produces,parameters,responses,schemes,deprecated,security,*")
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 List<String>
141      tags,
142      schemes;
143   private List<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.summary = copyFrom.summary;
164      this.description = copyFrom.description;
165      this.operationId = copyFrom.operationId;
166      this.deprecated = copyFrom.deprecated;
167      this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy();
168      this.tags = newList(copyFrom.tags);
169      this.schemes = newList(copyFrom.schemes);
170      this.consumes = newList(copyFrom.consumes);
171      this.produces = newList(copyFrom.produces);
172
173      if (copyFrom.parameters == null) {
174         this.parameters = null;
175      } else {
176         this.parameters = new ArrayList<>();
177         for (ParameterInfo p : copyFrom.parameters)
178            this.parameters.add(p.copy());
179      }
180
181      if (copyFrom.security == null) {
182         this.security = null;
183      } else {
184         this.security = new ArrayList<>();
185         for (Map<String,List<String>> m : copyFrom.security) {
186            Map<String,List<String>> m2 = new LinkedHashMap<>();
187            for (Map.Entry<String,List<String>> e : m.entrySet())
188               m2.put(e.getKey(), newList(e.getValue()));
189            this.security.add(m2);
190         }
191      }
192
193      if (copyFrom.responses == null) {
194         this.responses = null;
195      } else {
196         this.responses = new LinkedHashMap<>();
197         for (Map.Entry<String,ResponseInfo> e : copyFrom.responses.entrySet())
198            this.responses.put(e.getKey(), e.getValue().copy());
199      }
200   }
201
202   /**
203    * Make a deep copy of this object.
204    *
205    * @return A deep copy of this object.
206    */
207   public Operation copy() {
208      return new Operation(this);
209   }
210
211   /**
212    * Bean property getter:  <property>tags</property>.
213    *
214    * <p>
215    * A list of tags for API documentation control.
216    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
217    *
218    * @return The property value, or <jk>null</jk> if it is not set.
219    */
220   public List<String> getTags() {
221      return tags;
222   }
223
224   /**
225    * Bean property setter:  <property>tags</property>.
226    *
227    * <p>
228    * A list of tags for API documentation control.
229    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
230    *
231    * @param value
232    *    The new value for this property.
233    *    <br>Can be <jk>null</jk> to unset the property.
234    * @return This object (for method chaining).
235    */
236   public Operation setTags(Collection<String> value) {
237      tags = newList(value);
238      return this;
239   }
240
241   /**
242    * Adds one or more values to the <property>tags</property> property.
243    *
244    * <p>
245    * A list of tags for API documentation control.
246    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
247    *
248    * @param value
249    *    The values to add to this property.
250    *    <br>Ignored if <jk>null</jk>.
251    * @return This object (for method chaining).
252    */
253   public Operation addTags(Collection<String> value) {
254      tags = addToList(tags, value);
255      return this;
256   }
257
258   /**
259    * Same as {@link #addTags(Collection)}.
260    *
261    * @param values
262    *    The values to add to this property.
263    *    <br>Valid types:
264    *    <ul>
265    *       <li><c>Collection&lt;String&gt;</c>
266    *       <li><c>String</c> - JSON array representation of <c>Collection&lt;String&gt;</c>
267    *          <h5 class='figure'>Example:</h5>
268    *          <p class='bcode w800'>
269    *    tags(<js>"['foo','bar']"</js>);
270    *          </p>
271    *       <li><c>String</c> - Individual values
272    *          <h5 class='figure'>Example:</h5>
273    *          <p class='bcode w800'>
274    *    tags(<js>"foo"</js>, <js>"bar"</js>);
275    *          </p>
276    *    </ul>
277    * @return This object (for method chaining).
278    */
279   public Operation tags(Object...values) {
280      tags = addToList(tags, values, String.class);
281      return this;
282   }
283
284   /**
285    * Bean property getter:  <property>summary</property>.
286    *
287    * <p>
288    * A short summary of what the operation does.
289    *
290    * @return The property value, or <jk>null</jk> if it is not set.
291    */
292   public String getSummary() {
293      return summary;
294   }
295
296   /**
297    * Bean property setter:  <property>summary</property>.
298    *
299    * <p>
300    * A short summary of what the operation does.
301    *
302    * @param value
303    *    The new value for this property.
304    *    <br>Can be <jk>null</jk> to unset the property.
305    * @return This object (for method chaining).
306    */
307   public Operation setSummary(String value) {
308      summary = value;
309      return this;
310   }
311
312   /**
313    * Same as {@link #setSummary(String)}.
314    *
315    * @param value
316    *    The new value for this property.
317    *    <br>Non-String values will be converted to String using <c>toString()</c>.
318    *    <br>Can be <jk>null</jk> to unset the property.
319    * @return This object (for method chaining).
320    */
321   public Operation summary(Object value) {
322      return setSummary(stringify(value));
323   }
324
325   /**
326    * Bean property getter:  <property>description</property>.
327    *
328    * <p>
329    * A verbose explanation of the operation behavior.
330    *
331    * @return The property value, or <jk>null</jk> if it is not set.
332    */
333   public String getDescription() {
334      return description;
335   }
336
337   /**
338    * Bean property setter:  <property>description</property>.
339    *
340    * <p>
341    * A verbose explanation of the operation behavior.
342    *
343    * @param value
344    *    The new value for this property.
345    *    <br>{@doc GFM} can be used for rich text representation.
346    *    <br>Can be <jk>null</jk> to unset the property.
347    * @return This object (for method chaining).
348    */
349   public Operation setDescription(String value) {
350      description = value;
351      return this;
352   }
353
354   /**
355    * Same as {@link #setDescription(String)}.
356    *
357    * @param value
358    *    The new value for this property.
359    *    <br>Non-String values will be converted to String using <c>toString()</c>.
360    *    <br>{@doc GFM} can be used for rich text representation.
361    *    <br>Can be <jk>null</jk> to unset the property.
362    * @return This object (for method chaining).
363    */
364   public Operation description(Object value) {
365      return setDescription(stringify(value));
366   }
367
368   /**
369    * Bean property getter:  <property>externalDocs</property>.
370    *
371    * <p>
372    * Additional external documentation for this operation.
373    *
374    * @return The property value, or <jk>null</jk> if it is not set.
375    */
376   public ExternalDocumentation getExternalDocs() {
377      return externalDocs;
378   }
379
380   /**
381    * Bean property setter:  <property>externalDocs</property>.
382    *
383    * <p>
384    * Additional external documentation for this operation.
385    *
386    * @param value
387    *    The values to add to this property.
388    *    <br>Can be <jk>null</jk> to unset the property.
389    * @return This object (for method chaining).
390    */
391   public Operation setExternalDocs(ExternalDocumentation value) {
392      externalDocs = value;
393      return this;
394   }
395
396   /**
397    * Same as {@link #setExternalDocs(ExternalDocumentation)}.
398    *
399    * @param value
400    *    The new value for this property.
401    *    <br>Valid types:
402    *    <ul>
403    *       <li>{@link ExternalDocumentation}
404    *       <li><c>String</c> - JSON object representation of {@link ExternalDocumentation}
405    *          <h5 class='figure'>Example:</h5>
406    *          <p class='bcode w800'>
407    *    externalDocs(<js>"{description:'description',url:'url'}"</js>);
408    *          </p>
409    *    </ul>
410    *    <br>Can be <jk>null</jk> to unset the property.
411    * @return This object (for method chaining).
412    */
413   public Operation externalDocs(Object value) {
414      return setExternalDocs(toType(value, ExternalDocumentation.class));
415   }
416
417   /**
418    * Bean property getter:  <property>operationId</property>.
419    *
420    * <p>
421    * Unique string used to identify the operation.
422    *
423    * @return The property value, or <jk>null</jk> if it is not set.
424    */
425   public String getOperationId() {
426      return operationId;
427   }
428
429   /**
430    * Bean property setter:  <property>operationId</property>.
431    *
432    * <p>
433    * Unique string used to identify the operation.
434    *
435    * @param value
436    *    The new value for this property.
437    *    <br>The id MUST be unique among all operations described in the API.
438    *    <br>Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is recommended to
439    *    follow common programming naming conventions.
440    *    <br>Can be <jk>null</jk> to unset the property.
441    * @return This object (for method chaining).
442    */
443   public Operation setOperationId(String value) {
444      operationId = value;
445      return this;
446   }
447
448   /**
449    * Same as {@link #setOperationId(String)}.
450    *
451    * @param value
452    *    The new value for this property.
453    *    <br>The id MUST be unique among all operations described in the API.
454    *    <br>Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is recommended to
455    *    follow common programming naming conventions.
456    *    <br>Non-String values will be converted to String using <c>toString()</c>.
457    *    <br>Can be <jk>null</jk> to unset the property.
458    * @return This object (for method chaining).
459    */
460   public Operation operationId(Object value) {
461      return setOperationId(stringify(value));
462   }
463
464   /**
465    * Bean property getter:  <property>consumes</property>.
466    *
467    * <p>
468    * A list of MIME types the operation can consume.
469    *
470    * <p>
471    * This overrides the <c>consumes</c> definition at the Swagger Object.
472    * <br>An empty value MAY be used to clear the global definition.
473    *
474    * @return The property value, or <jk>null</jk> if it is not set.
475    */
476   public List<MediaType> getConsumes() {
477      return consumes;
478   }
479
480   /**
481    * Bean property setter:  <property>consumes</property>.
482    *
483    * <p>
484    * A list of MIME types the operation can consume.
485    *
486    * <p>
487    * This overrides the <c>consumes</c> definition at the Swagger Object.
488    * <br>An empty value MAY be used to clear the global definition.
489    *
490    * @param value
491    *    The new value for this property.
492    *    <br>Values MUST be as described under {@doc SwaggerMimeTypes}.
493    *    <br>Can be <jk>null</jk> to unset the property.
494    * @return This object (for method chaining).
495    */
496   public Operation setConsumes(Collection<MediaType> value) {
497      consumes = newList(value);
498      return this;
499   }
500
501   /**
502    * Adds one or more values to the <property>consumes</property> property.
503    *
504    * <p>
505    * A list of MIME types the operation can consume.
506    *
507    * <p>
508    * This overrides the <c>consumes</c> definition at the Swagger Object.
509    * <br>An empty value MAY be used to clear the global definition.
510    *
511    * @param value
512    *    The values to add to this property.
513    *    <br>Values MUST be as described under {@doc SwaggerMimeTypes}.
514    *    <br>Ignored if <jk>null</jk>.
515    * @return This object (for method chaining).
516    */
517   public Operation addConsumes(Collection<MediaType> value) {
518      consumes = addToList(consumes, value);
519      return this;
520   }
521
522   /**
523    * Adds one or more values to the <property>consumes</property> property.
524    *
525    * @param values
526    *    The values to add to this property.
527    *    <br>Valid types:
528    *    <ul>
529    *       <li>{@link MediaType}
530    *       <li><c>Collection&lt;{@link MediaType}|String&gt;</c>
531    *       <li><c>{@link MediaType}[]</c>
532    *       <li><c>String</c> - JSON array representation of <c>Collection&lt;{@link MediaType}&gt;</c>
533    *          <h5 class='figure'>Example:</h5>
534    *          <p class='bcode w800'>
535    *    consumes(<js>"['text/json']"</js>);
536    *          </p>
537    *       <li><c>String</c> - Individual values
538    *          <h5 class='figure'>Example:</h5>
539    *          <p class='bcode w800'>
540    *    consumes(<js>"text/json"</js>);
541    *          </p>
542    *    </ul>
543    *    <br>Ignored if <jk>null</jk>.
544    * @return This object (for method chaining).
545    */
546   public Operation consumes(Object...values) {
547      consumes = addToList(consumes, values, MediaType.class);
548      return this;
549   }
550
551   /**
552    * Bean property getter:  <property>produces</property>.
553    *
554    * <p>
555    * A list of MIME types the operation can produce.
556    *
557    * <p>
558    * This overrides the <c>produces</c> definition at the Swagger Object.
559    * <br>An empty value MAY be used to clear the global definition.
560    *
561    * @return The property value, or <jk>null</jk> if it is not set.
562    */
563   public List<MediaType> getProduces() {
564      return produces;
565   }
566
567   /**
568    * Bean property setter:  <property>produces</property>.
569    *
570    * <p>
571    * A list of MIME types the operation can produce.
572    *
573    * <p>
574    * This overrides the <c>produces</c> definition at the Swagger Object.
575    * <br>An empty value MAY be used to clear the global definition.
576    *
577    * @param value
578    *    The values to add to this property.
579    *    <br>Value MUST be as described under {@doc SwaggerMimeTypes}.
580    *    <br>Can be <jk>null</jk> to unset the property.
581    * @return This object (for method chaining).
582    */
583   public Operation setProduces(Collection<MediaType> value) {
584      produces = newList(value);
585      return this;
586   }
587
588   /**
589    * Adds one or more values to the <property>produces</property> property.
590    *
591    * <p>
592    * A list of MIME types the operation can produces.
593    *
594    * <p>
595    * This overrides the <c>produces</c> definition at the Swagger Object.
596    * <br>An empty value MAY be used to clear the global definition.
597    *
598    * @param value
599    *    The values to add to this property.
600    *    <br>Ignored if <jk>null</jk>.
601    * @return This object (for method chaining).
602    */
603   public Operation addProduces(Collection<MediaType> value) {
604      produces = addToList(produces, value);
605      return this;
606   }
607
608   /**
609    * Same as {@link #addProduces(Collection)}.
610    *
611    * @param values
612    *    The values to add to this property.
613    *    <br>Valid types:
614    *    <ul>
615    *       <li>{@link MediaType}
616    *       <li><c>Collection&lt;{@link MediaType}|String&gt;</c>
617    *       <li><c>{@link MediaType}[]</c>
618    *       <li><c>String</c> - JSON array representation of <c>Collection&lt;{@link MediaType}&gt;</c>
619    *          <h5 class='figure'>Example:</h5>
620    *          <p class='bcode w800'>
621    *    produces(<js>"['text/json']"</js>);
622    *          </p>
623    *       <li><c>String</c> - Individual values
624    *          <h5 class='figure'>Example:</h5>
625    *          <p class='bcode w800'>
626    *    produces(<js>"text/json"</js>);
627    *          </p>
628    *    </ul>
629    *    <br>Ignored if <jk>null</jk>.
630    * @return This object (for method chaining).
631    */
632   public Operation produces(Object...values) {
633      produces = addToList(produces, values, MediaType.class);
634      return this;
635   }
636
637   /**
638    * Bean property getter:  <property>parameters</property>.
639    *
640    * <p>
641    * A list of parameters that are applicable for this operation.
642    *
643    * <ul class='notes'>
644    *    <li>
645    *       If a parameter is already defined at the {@doc SwaggerPathItemObject Path Item},
646    *       the new definition will override it, but can never remove it.
647    *    <li>
648    *       The list MUST NOT include duplicated parameters.
649    *    <li>
650    *       A unique parameter is defined by a combination of a <c>name</c> and <c>location</c>.
651    *    <li>
652    *       The list can use the {@doc SwaggerReferenceObject}
653    *       to link to parameters that are defined at the {@doc SwaggerParameterObject Swagger Object's parameters}.
654    *    <li>
655    *       There can be one <js>"body"</js> parameter at most.
656    * </ul>
657    *
658    * @return The property value, or <jk>null</jk> if it is not set.
659    */
660   public List<ParameterInfo> getParameters() {
661      return parameters;
662   }
663
664   /**
665    * Returns the parameter with the specified type and name.
666    *
667    * @param in The parameter in.
668    * @param name The parameter name.  Can be <jk>null</jk> for parameter type <c>body</c>.
669    * @return The matching parameter info, or <jk>null</jk> if not found.
670    */
671   public ParameterInfo getParameter(String in, String name) {
672      if (parameters != null)
673         for (ParameterInfo pi : parameters)
674            if (StringUtils.isEquals(pi.getIn(), in))
675               if (StringUtils.isEquals(pi.getName(), name) || "body".equals(pi.getIn()))
676                  return pi;
677      return null;
678   }
679
680   /**
681    * Bean property setter:  <property>parameters</property>.
682    *
683    * <p>
684    * A list of parameters that are applicable for this operation.
685    *
686    * <ul class='notes'>
687    *    <li>
688    *       If a parameter is already defined at the {@doc SwaggerPathItemObject Path Item},
689    *       the new definition will override it, but can never remove it.
690    *    <li>
691    *       The list MUST NOT include duplicated parameters.
692    *    <li>
693    *       A unique parameter is defined by a combination of a <c>name</c> and <c>location</c>.
694    *    <li>
695    *       The list can use the {@doc SwaggerReferenceObject}
696    *       to link to parameters that are defined at the {@doc SwaggerParameterObject Swagger Object's parameters}.
697    *    <li>
698    *       There can be one <js>"body"</js> parameter at most.
699    * </ul>
700    *
701    * @param value
702    *    The new value for this property.
703    *    <br>Can be <jk>null</jk> to unset the property.
704    * @return This object (for method chaining).
705    */
706   public Operation setParameters(Collection<ParameterInfo> value) {
707      parameters = newList(value);
708      return this;
709   }
710
711   /**
712    * Adds one or more values to the <property>parameters</property> property.
713    *
714    * <p>
715    * A list of parameters that are applicable for this operation.
716    *
717    * <ul class='notes'>
718    *    <li>
719    *       If a parameter is already defined at the {@doc SwaggerPathItemObject Path Item},
720    *       the new definition will override it, but can never remove it.
721    *    <li>
722    *       The list MUST NOT include duplicated parameters.
723    *    <li>
724    *       A unique parameter is defined by a combination of a <c>name</c> and <c>location</c>.
725    *    <li>
726    *       The list can use the {@doc SwaggerReferenceObject}
727    *       to link to parameters that are defined at the {@doc SwaggerParameterObject Swagger Object's parameters}.
728    *    <li>
729    *       There can be one <js>"body"</js> parameter at most.
730    * </ul>
731    *
732    * @param value
733    *    The values to add to this property.
734    *    <br>Ignored if <jk>null</jk>.
735    * @return This object (for method chaining).
736    */
737   public Operation addParameters(Collection<ParameterInfo> value) {
738      parameters = addToList(parameters, value);
739      return this;
740   }
741
742   /**
743    * Same as {@link #addParameters(Collection)}.
744    *
745    * @param values
746    *    The values to add to this property.
747    *    <br>Valid types:
748    *    <ul>
749    *       <li>{@link ParameterInfo}
750    *       <li><c>Collection&lt;{@link ParameterInfo}|String&gt;</c>
751    *       <li><c>String</c> - JSON array representation of <c>Collection&lt;{@link ParameterInfo}&gt;</c>
752    *          <h5 class='figure'>Example:</h5>
753    *          <p class='bcode w800'>
754    *    parameters(<js>"[{path:'path',id:'id'}]"</js>);
755    *          </p>
756    *       <li><c>String</c> - JSON object representation of {@link ParameterInfo}
757    *          <h5 class='figure'>Example:</h5>
758    *          <p class='bcode w800'>
759    *    parameters(<js>"{path:'path',id:'id'}"</js>);
760    *          </p>
761    *    </ul>
762    *    <br>Ignored if <jk>null</jk>.
763    * @return This object (for method chaining).
764    */
765   public Operation parameters(Object...values) {
766      parameters = addToList(parameters, values, ParameterInfo.class);
767      return this;
768   }
769
770   /**
771    * Bean property getter:  <property>responses</property>.
772    *
773    * <p>
774    * The list of possible responses as they are returned from executing this operation.
775    *
776    * @return The property value, or <jk>null</jk> if it is not set.
777    */
778   public Map<String,ResponseInfo> getResponses() {
779      return responses;
780   }
781
782
783   /**
784    * Returns the response info with the given status code.
785    *
786    * @param status The HTTP status code.
787    * @return The response info, or <jk>null</jk> if not found.
788    */
789   public ResponseInfo getResponse(Object status) {
790      if (responses != null)
791         return responses.get(String.valueOf(status));
792      return null;
793   }
794
795   /**
796    * Bean property setter:  <property>responses</property>.
797    *
798    * <p>
799    * The list of possible responses as they are returned from executing this operation.
800    *
801    * @param value
802    *    The new value for this property.
803    *    <br>Property value is required.
804    * @return This object (for method chaining).
805    */
806   public Operation setResponses(Map<String,ResponseInfo> value) {
807      responses = newMap(value);
808      return this;
809   }
810
811   /**
812    * Adds one or more values to the <property>responses</property> property.
813    *
814    * <p>
815    * The list of possible responses as they are returned from executing this operation.
816    *
817    * @param values
818    *    The values to add to this property.
819    *    <br>Ignored if <jk>null</jk>.
820    * @return This object (for method chaining).
821    */
822   public Operation addResponses(Map<String,ResponseInfo> values) {
823      responses = addToMap(responses, values);
824      return this;
825   }
826
827   /**
828    * Adds a single value to the <property>responses</property> property.
829    *
830    * @param statusCode The HTTP status code.
831    * @param response The response description.
832    * @return This object (for method chaining).
833    */
834   public Operation response(String statusCode, ResponseInfo response) {
835      return addResponses(Collections.singletonMap(statusCode, response));
836   }
837
838   /**
839    * Same as {@link #addResponses(Map)}.
840    *
841    * @param value
842    *    The new value for this property.
843    *    <br>Valid types:
844    *    <ul>
845    *       <li><c>Map&lt;Integer,{@link ResponseInfo}|String&gt;</c>
846    *       <li><c>String</c> - JSON object representation of <c>Map&lt;Integer,{@link ResponseInfo}&gt;</c>
847    *          <h5 class='figure'>Example:</h5>
848    *          <p class='bcode w800'>
849    *    responses(<js>"{'404':{description:'description',...}}"</js>);
850    *          </p>
851    *    </ul>
852    * @return This object (for method chaining).
853    */
854   public Operation responses(Object...value) {
855      responses = addToMap(responses, value, String.class, ResponseInfo.class);
856      return this;
857   }
858
859   /**
860    * Bean property getter:  <property>schemes</property>.
861    *
862    * <p>
863    * The transfer protocol for the operation.
864    * <br>The value overrides the Swagger Object <c>schemes</c> definition.
865    *
866    * @return The property value, or <jk>null</jk> if it is not set.
867    */
868   public List<String> getSchemes() {
869      return schemes;
870   }
871
872   /**
873    * Bean property setter:  <property>schemes</property>.
874    *
875    * <p>
876    * The transfer protocol for the operation.
877    * <br>The value overrides the Swagger Object <c>schemes</c> definition.
878    *
879    * @param value
880    *    The new value for this property.
881    *    <br>Valid values:
882    *    <ul>
883    *       <li><js>"http"</js>
884    *       <li><js>"https"</js>
885    *       <li><js>"ws"</js>
886    *       <li><js>"wss"</js>
887    *    </ul>
888    *    <br>Can be <jk>null</jk> to unset the property.
889    * @return This object (for method chaining).
890    */
891   public Operation setSchemes(Collection<String> value) {
892      schemes = newList(value);
893      return this;
894   }
895
896   /**
897    * Adds one or more values to the <property>schemes</property> property.
898    *
899    * <p>
900    * The transfer protocol for the operation.
901    * <br>The value overrides the Swagger Object <c>schemes</c> definition.
902    *
903    * @param value
904    *    The values to add to this property.
905    *    <br>Ignored if <jk>null</jk>.
906    * @return This object (for method chaining).
907    */
908   public Operation addSchemes(Collection<String> value) {
909      schemes = addToList(schemes, value);
910      return this;
911   }
912
913   /**
914    * Same as {@link #addSchemes(Collection)}.
915    *
916    * @param values
917    *    The new value for this property.
918    *    <br>Valid types:
919    *    <ul>
920    *       <li><c>Collection&lt;String&gt;</c>
921    *       <li><c>String</c> - JSON array representation of <c>Collection&lt;String&gt;</c>
922    *          <h5 class='figure'>Example:</h5>
923    *          <p class='bcode w800'>
924    *    schemes(<js>"['scheme1','scheme2']"</js>);
925    *          </p>
926    *       <li><c>String</c> - Individual values
927    *          <h5 class='figure'>Example:</h5>
928    *          <p class='bcode w800'>
929    *    schemes(<js>"scheme1</js>, <js>"scheme2"</js>);
930    *          </p>
931    *    </ul>
932    * @return This object (for method chaining).
933    */
934   public Operation schemes(Object...values) {
935      schemes = addToList(schemes, values, String.class);
936      return this;
937   }
938
939   /**
940    * Bean property getter:  <property>deprecated</property>.
941    *
942    * <p>
943    * Declares this operation to be deprecated.
944    *
945    * @return The property value, or <jk>null</jk> if it is not set.
946    */
947   public Boolean getDeprecated() {
948      return deprecated;
949   }
950
951   /**
952    * Bean property getter:  <property>deprecated</property>.
953    *
954    * <p>
955    * Declares this operation to be deprecated.
956    *
957    * @return The property value, or <jk>false</jk> if it is not set.
958    */
959   public boolean isDeprecated() {
960      return deprecated != null && deprecated == true;
961   }
962
963   /**
964    * Bean property setter:  <property>deprecated</property>.
965    *
966    * <p>
967    * Declares this operation to be deprecated.
968    *
969    * @param value T
970    *    The new value for this property.
971    * @return This object (for method chaining).
972    */
973   public Operation setDeprecated(Boolean value) {
974      deprecated = value;
975      return this;
976   }
977
978   /**
979    * Same as {@link #setDeprecated(Boolean)}.
980    *
981    * @param value
982    *    The new value for this property.
983    *    <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
984    *    <br>Can be <jk>null</jk> to unset the property.
985    * @return This object (for method chaining).
986    */
987   public Operation deprecated(Object value) {
988      return setDeprecated(toBoolean(value));
989   }
990
991   /**
992    * Bean property getter:  <property>security</property>.
993    *
994    * <p>
995    * A declaration of which security schemes are applied for this operation.
996    * <br>The list of values describes alternative security schemes that can be used (that is, there is a logical OR
997    * between the security requirements).
998    *
999    * <p>
1000    * This definition overrides any declared top-level security.
1001    * <br>To remove a top-level <c>security</c> declaration, an empty array can be used.
1002    *
1003    * @return The property value, or <jk>null</jk> if it is not set.
1004    */
1005   public List<Map<String,List<String>>> getSecurity() {
1006      return security;
1007   }
1008
1009   /**
1010    * Bean property setter:  <property>security</property>.
1011    *
1012    * <p>
1013    * A declaration of which security schemes are applied for this operation.
1014    * <br>The list of values describes alternative security schemes that can be used (that is, there is a logical OR
1015    * between the security requirements).
1016    *
1017    * <p>
1018    * This definition overrides any declared top-level security.
1019    * <br>To remove a top-level <c>security</c> declaration, an empty array can be used.
1020    *
1021    * @param value
1022    *    The new value for this property.
1023    *    <br>Can be <jk>null</jk> to unset the property.
1024    * @return This object (for method chaining).
1025    */
1026   public Operation setSecurity(Collection<Map<String,List<String>>> value) {
1027      security = newList(value);
1028      return this;
1029   }
1030
1031   /**
1032    * Adds one or more values to the <property>security</property> property.
1033    *
1034    * <p>
1035    * A declaration of which security schemes are applied for this operation.
1036    * <br>The list of values describes alternative security schemes that can be used (that is, there is a logical OR
1037    * between the security requirements).
1038    *
1039    * <p>
1040    * This definition overrides any declared top-level security.
1041    * <br>To remove a top-level <c>security</c> declaration, an empty array can be used.
1042    *
1043    * @param values
1044    *    The values to add to this property.
1045    *    <br>Ignored if <jk>null</jk>.
1046    * The new value for this property.
1047    * @return This object (for method chaining).
1048    */
1049   public Operation addSecurity(Collection<Map<String,List<String>>> values) {
1050      security = addToList(security, values);
1051      return this;
1052   }
1053
1054   /**
1055    * Same as {@link #addSecurity(Collection)}.
1056    *
1057    * @param scheme
1058    *    The scheme name.
1059    * @param alternatives
1060    *    The list of values describes alternative security schemes that can be used (that is, there is a logical OR
1061    *    between the security requirements).
1062    * @return This object (for method chaining).
1063    */
1064   public Operation security(String scheme, String...alternatives) {
1065      Map<String,List<String>> m = new LinkedHashMap<>();
1066      m.put(scheme, Arrays.asList(alternatives));
1067      return addSecurity(Collections.singletonList(m));
1068   }
1069
1070   /**
1071    * Same as {@link #addSecurity(Collection)}.
1072    *
1073    * @param value
1074    *    The new value for this property.
1075    *    <br>Valid types:
1076    *    <ul>
1077    *       <li><c>Map&lt;String,List&lt;String&gt;&gt;</c>
1078    *       <li><c>String</c> - JSON object representation of a <c>Map&lt;String,List&lt;String&gt;&gt;</c>
1079    *       <h5 class='figure'>Example:</h5>
1080    *       <p class='bcode w800'>
1081    *    securities(<js>"{key:['val1','val2']}"</js>);
1082    *       </p>
1083    *    </ul>
1084    * @return This object (for method chaining).
1085    */
1086   @SuppressWarnings({ "unchecked", "rawtypes" })
1087   public Operation securities(Object...value) {
1088      security = addToList((List)security, value, Map.class, String.class, List.class, String.class);
1089      return this;
1090   }
1091
1092   /**
1093    * Returns <jk>true</jk> if the summary property is not null or empty.
1094    *
1095    * @return <jk>true</jk> if the summary property is not null or empty.
1096    */
1097   public boolean hasSummary() {
1098      return isNotEmpty(summary);
1099   }
1100
1101   /**
1102    * Returns <jk>true</jk> if the description property is not null or empty.
1103    *
1104    * @return <jk>true</jk> if the description property is not null or empty.
1105    */
1106   public boolean hasDescription() {
1107      return isNotEmpty(description);
1108   }
1109
1110   /**
1111    * Returns <jk>true</jk> if this operation has the specified tag associated with it.
1112    *
1113    * @param name The tag name.
1114    * @return <jk>true</jk> if this operation has the specified tag associated with it.
1115    */
1116   public boolean hasTag(String name) {
1117      return tags != null && tags.contains(name);
1118   }
1119
1120   /**
1121    * Returns <jk>true</jk> if this operation has no tags associated with it.
1122    *
1123    * @return <jk>true</jk> if this operation has no tags associated with it.
1124    */
1125   public boolean hasNoTags() {
1126      return tags == null || tags.isEmpty();
1127   }
1128
1129   /**
1130    * Returns <jk>true</jk> if this operation has parameters associated with it.
1131    *
1132    * @return <jk>true</jk> if this operation has parameters associated with it.
1133    */
1134   public boolean hasParameters() {
1135      return parameters != null && ! parameters.isEmpty();
1136   }
1137
1138   /**
1139    * Returns <jk>true</jk> if this operation has responses associated with it.
1140    *
1141    * @return <jk>true</jk> if this operation has responses associated with it.
1142    */
1143   public boolean hasResponses() {
1144      return responses != null && ! responses.isEmpty();
1145   }
1146
1147   @Override /* SwaggerElement */
1148   public <T> T get(String property, Class<T> type) {
1149      if (property == null)
1150         return null;
1151      switch (property) {
1152         case "tags": return toType(getTags(), type);
1153         case "summary": return toType(getSummary(), type);
1154         case "description": return toType(getDescription(), type);
1155         case "externalDocs": return toType(getExternalDocs(), type);
1156         case "operationId": return toType(getOperationId(), type);
1157         case "consumes": return toType(getConsumes(), type);
1158         case "produces": return toType(getProduces(), type);
1159         case "parameters": return toType(getParameters(), type);
1160         case "responses": return toType(getResponses(), type);
1161         case "schemes": return toType(getSchemes(), type);
1162         case "deprecated": return toType(getDeprecated(), type);
1163         case "security": return toType(getSecurity(), type);
1164         default: return super.get(property, type);
1165      }
1166   }
1167
1168   @Override /* SwaggerElement */
1169   public Operation set(String property, Object value) {
1170      if (property == null)
1171         return this;
1172      switch (property) {
1173         case "tags": return setTags(null).tags(value);
1174         case "summary": return summary(value);
1175         case "description": return description(value);
1176         case "externalDocs": return externalDocs(value);
1177         case "operationId": return operationId(value);
1178         case "consumes": return setConsumes(null).consumes(value);
1179         case "produces": return setProduces(null).produces(value);
1180         case "parameters": return setParameters(null).parameters(value);
1181         case "responses": return setResponses(null).responses(value);
1182         case "schemes": return setSchemes(null).schemes(value);
1183         case "deprecated": return deprecated(value);
1184         case "security": return setSecurity(null).securities(value);
1185         default:
1186            super.set(property, value);
1187            return this;
1188      }
1189   }
1190
1191   @Override /* SwaggerElement */
1192   public Set<String> keySet() {
1193      ASet<String> s = new ASet<String>()
1194         .appendIf(tags != null, "tags")
1195         .appendIf(summary != null, "summary")
1196         .appendIf(description != null, "description")
1197         .appendIf(externalDocs != null, "externalDocs")
1198         .appendIf(operationId != null, "operationId")
1199         .appendIf(consumes != null, "consumes")
1200         .appendIf(produces != null, "produces")
1201         .appendIf(parameters != null, "parameters")
1202         .appendIf(responses != null, "responses")
1203         .appendIf(schemes != null, "schemes")
1204         .appendIf(deprecated != null, "deprecated")
1205         .appendIf(security != null, "security");
1206      return new MultiSet<>(s, super.keySet());
1207   }
1208}