001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.openapi3;
018
019import static org.apache.juneau.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021import static org.apache.juneau.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.*;
026import org.apache.juneau.common.utils.*;
027import org.apache.juneau.internal.*;
028import org.apache.juneau.marshaller.*;
029
030/**
031 * Describes a single operation parameter.
032 *
033 * <p>
034 * The Parameter Object describes a single parameter used in an API operation. Parameters can be passed in various 
035 * locations including the path, query string, headers, or cookies. Each parameter has a name, location, and schema 
036 * that defines its type and constraints.
037 *
038 * <h5 class='section'>OpenAPI Specification:</h5>
039 * <p>
040 * The Parameter Object is composed of the following fields:
041 * <ul class='spaced-list'>
042 *    <li><c>name</c> (string, REQUIRED) - The name of the parameter
043 *    <li><c>in</c> (string, REQUIRED) - The location of the parameter. Possible values: <js>"query"</js>, <js>"header"</js>, <js>"path"</js>, or <js>"cookie"</js>
044 *    <li><c>description</c> (string) - A brief description of the parameter (CommonMark syntax may be used)
045 *    <li><c>required</c> (boolean) - Determines whether this parameter is mandatory (must be <jk>true</jk> if <c>in</c> is <js>"path"</js>)
046 *    <li><c>deprecated</c> (boolean) - Specifies that a parameter is deprecated
047 *    <li><c>allowEmptyValue</c> (boolean) - Sets the ability to pass empty-valued parameters (valid only for <js>"query"</js> parameters)
048 *    <li><c>style</c> (string) - Describes how the parameter value will be serialized
049 *    <li><c>explode</c> (boolean) - When true, parameter values of type array or object generate separate parameters for each value
050 *    <li><c>allowReserved</c> (boolean) - Determines whether the parameter value should allow reserved characters
051 *    <li><c>schema</c> ({@link SchemaInfo}) - The schema defining the type used for the parameter
052 *    <li><c>example</c> (any) - Example of the parameter's potential value
053 *    <li><c>examples</c> (map of {@link Example}) - Examples of the parameter's potential value
054 * </ul>
055 *
056 * <h5 class='section'>Example:</h5>
057 * <p class='bjava'>
058 *    <jc>// Create a query parameter</jc>
059 *    Parameter <jv>param</jv> = <jk>new</jk> Parameter()
060 *       .setName(<js>"status"</js>)
061 *       .setIn(<js>"query"</js>)
062 *       .setDescription(<js>"Status values to filter by"</js>)
063 *       .setRequired(<jk>false</jk>)
064 *       .setSchema(
065 *          <jk>new</jk> SchemaInfo()
066 *             .setType(<js>"array"</js>)
067 *             .setItems(
068 *                <jk>new</jk> Items().setType(<js>"string"</js>)
069 *             )
070 *       )
071 *       .setStyle(<js>"form"</js>)
072 *       .setExplode(<jk>true</jk>);
073 * </p>
074 *
075 * <h5 class='section'>See Also:</h5><ul>
076 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#parameter-object">OpenAPI Specification &gt; Parameter Object</a>
077 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a>
078 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
079 * </ul>
080 */
081public class Parameter extends OpenApiElement {
082
083   private static final String[] VALID_IN = {"query", "header", "path", "cookie"};
084   private static final String[] VALID_STYLES = {"matrix", "label", "form", "simple", "spaceDelimited", "pipeDelimited", "deepObject"};
085
086   private String name, in, description, style;
087   private Boolean required, deprecated, allowEmptyValue, explode, allowReserved;
088   private SchemaInfo schema;
089   private Object example;
090   private Map<String,Example> examples;
091
092   /**
093    * Default constructor.
094    */
095   public Parameter() {}
096
097   /**
098    * Copy constructor.
099    *
100    * @param copyFrom The object to copy.
101    */
102   public Parameter(Parameter copyFrom) {
103      super(copyFrom);
104      this.name = copyFrom.name;
105      this.in = copyFrom.in;
106      this.description = copyFrom.description;
107      this.style = copyFrom.style;
108      this.required = copyFrom.required;
109      this.deprecated = copyFrom.deprecated;
110      this.allowEmptyValue = copyFrom.allowEmptyValue;
111      this.explode = copyFrom.explode;
112      this.allowReserved = copyFrom.allowReserved;
113      this.schema = copyFrom.schema;
114      this.example = copyFrom.example;
115      this.examples = copyOf(copyFrom.examples);
116   }
117
118   /**
119    * Makes a copy of this object.
120    *
121    * @return A new copy of this object.
122    */
123   public Parameter copy() {
124      return new Parameter(this);
125   }
126
127   /**
128    * Returns the parameter name.
129    *
130    * @return The parameter name.
131    */
132   public String getName() {
133      return name;
134   }
135
136   /**
137    * Sets the parameter name.
138    *
139    * @param value The new value for this property.
140    * @return This object.
141    */
142   public Parameter setName(String value) {
143      this.name = value;
144      return this;
145   }
146
147   /**
148    * Returns the parameter location.
149    *
150    * @return The parameter location.
151    */
152   public String getIn() {
153      return in;
154   }
155
156   /**
157    * Sets the parameter location.
158    *
159    * @param value The new value for this property.
160    * @return This object.
161    */
162   public Parameter setIn(String value) {
163      if (isStrict() && ! contains(value, VALID_IN))
164         throw new BasicRuntimeException(
165            "Invalid value passed in to setIn(String).  Value=''{0}'', valid values={1}",
166            value, Json5.of(VALID_IN)
167         );
168      this.in = value;
169      return this;
170   }
171
172   /**
173    * Returns the description.
174    *
175    * @return The description.
176    */
177   public String getDescription() {
178      return description;
179   }
180
181   /**
182    * Sets the description.
183    *
184    * @param value The new value for this property.
185    * @return This object.
186    */
187   public Parameter setDescription(String value) {
188      this.description = value;
189      return this;
190   }
191
192   /**
193    * Returns the style.
194    *
195    * @return The style.
196    */
197   public String getStyle() {
198      return style;
199   }
200
201   /**
202    * Sets the style.
203    *
204    * @param value The new value for this property.
205    * @return This object.
206    */
207   public Parameter setStyle(String value) {
208      if (isStrict() && ! contains(value, VALID_STYLES))
209         throw new BasicRuntimeException(
210            "Invalid value passed in to setStyle(String).  Value=''{0}'', valid values={1}",
211            value, Json5.of(VALID_STYLES)
212         );
213      this.style = value;
214      return this;
215   }
216
217   /**
218    * Returns the required flag.
219    *
220    * @return The required flag.
221    */
222   public Boolean getRequired() {
223      return required;
224   }
225
226   /**
227    * Sets the required flag.
228    *
229    * @param value The new value for this property.
230    * @return This object.
231    */
232   public Parameter setRequired(Boolean value) {
233      this.required = value;
234      return this;
235   }
236
237   /**
238    * Returns the deprecated flag.
239    *
240    * @return The deprecated flag.
241    */
242   public Boolean getDeprecated() {
243      return deprecated;
244   }
245
246   /**
247    * Sets the deprecated flag.
248    *
249    * @param value The new value for this property.
250    * @return This object.
251    */
252   public Parameter setDeprecated(Boolean value) {
253      this.deprecated = value;
254      return this;
255   }
256
257   /**
258    * Returns the allow empty value flag.
259    *
260    * @return The allow empty value flag.
261    */
262   public Boolean getAllowEmptyValue() {
263      return allowEmptyValue;
264   }
265
266   /**
267    * Sets the allow empty value flag.
268    *
269    * @param value The new value for this property.
270    * @return This object.
271    */
272   public Parameter setAllowEmptyValue(Boolean value) {
273      this.allowEmptyValue = value;
274      return this;
275   }
276
277   /**
278    * Returns the explode flag.
279    *
280    * @return The explode flag.
281    */
282   public Boolean getExplode() {
283      return explode;
284   }
285
286   /**
287    * Sets the explode flag.
288    *
289    * @param value The new value for this property.
290    * @return This object.
291    */
292   public Parameter setExplode(Boolean value) {
293      this.explode = value;
294      return this;
295   }
296
297   /**
298    * Returns the allow reserved flag.
299    *
300    * @return The allow reserved flag.
301    */
302   public Boolean getAllowReserved() {
303      return allowReserved;
304   }
305
306   /**
307    * Sets the allow reserved flag.
308    *
309    * @param value The new value for this property.
310    * @return This object.
311    */
312   public Parameter setAllowReserved(Boolean value) {
313      this.allowReserved = value;
314      return this;
315   }
316
317   /**
318    * Returns the schema.
319    *
320    * @return The schema.
321    */
322   public SchemaInfo getSchema() {
323      return schema;
324   }
325
326   /**
327    * Sets the schema.
328    *
329    * @param value The new value for this property.
330    * @return This object.
331    */
332   public Parameter setSchema(SchemaInfo value) {
333      this.schema = value;
334      return this;
335   }
336
337   /**
338    * Returns the example.
339    *
340    * @return The example.
341    */
342   public Object getExample() {
343      return example;
344   }
345
346   /**
347    * Sets the example.
348    *
349    * @param value The new value for this property.
350    * @return This object.
351    */
352   public Parameter setExample(Object value) {
353      this.example = value;
354      return this;
355   }
356
357   /**
358    * Returns the examples map.
359    *
360    * @return The examples map.
361    */
362   public Map<String,Example> getExamples() {
363      return examples;
364   }
365
366   /**
367    * Sets the examples map.
368    *
369    * @param value The new value for this property.
370    * @return This object.
371    */
372   public Parameter setExamples(Map<String,Example> value) {
373      this.examples = value;
374      return this;
375   }
376
377   @Override /* Overridden from OpenApiElement */
378   public <T> T get(String property, Class<T> type) {
379      assertArgNotNull("property", property);
380      return switch (property) {
381         case "name" -> toType(getName(), type);
382         case "in" -> toType(getIn(), type);
383         case "description" -> toType(getDescription(), type);
384         case "required" -> toType(getRequired(), type);
385         case "deprecated" -> toType(getDeprecated(), type);
386         case "allowEmptyValue" -> toType(getAllowEmptyValue(), type);
387         case "style" -> toType(getStyle(), type);
388         case "explode" -> toType(getExplode(), type);
389         case "allowReserved" -> toType(getAllowReserved(), type);
390         case "schema" -> toType(getSchema(), type);
391         case "example" -> toType(getExample(), type);
392         case "examples" -> toType(getExamples(), type);
393         default -> super.get(property, type);
394      };
395   }
396
397   @Override /* Overridden from OpenApiElement */
398   public Parameter set(String property, Object value) {
399      assertArgNotNull("property", property);
400      return switch (property) {
401         case "allowEmptyValue" -> setAllowEmptyValue(toType(value, Boolean.class));
402         case "allowReserved" -> setAllowReserved(toType(value, Boolean.class));
403         case "description" -> setDescription(Utils.s(value));
404         case "deprecated" -> setDeprecated(toType(value, Boolean.class));
405         case "example" -> setExample(value);
406         case "examples" -> setExamples(mapBuilder(String.class, Example.class).sparse().addAny(value).build());
407         case "explode" -> setExplode(toType(value, Boolean.class));
408         case "in" -> setIn(Utils.s(value));
409         case "name" -> setName(Utils.s(value));
410         case "required" -> setRequired(toType(value, Boolean.class));
411         case "schema" -> setSchema(toType(value, SchemaInfo.class));
412         case "style" -> setStyle(Utils.s(value));
413         default -> {
414            super.set(property, value);
415            yield this;
416         }
417      };
418   }
419
420   @Override /* Overridden from OpenApiElement */
421   public Set<String> keySet() {
422      var s = setBuilder(String.class)
423         .addIf(allowEmptyValue != null, "allowEmptyValue")
424         .addIf(allowReserved != null, "allowReserved")
425         .addIf(description != null, "description")
426         .addIf(deprecated != null, "deprecated")
427         .addIf(example != null, "example")
428         .addIf(examples != null, "examples")
429         .addIf(explode != null, "explode")
430         .addIf(in != null, "in")
431         .addIf(name != null, "name")
432         .addIf(required != null, "required")
433         .addIf(schema != null, "schema")
434         .addIf(style != null, "style")
435         .build();
436      return new MultiSet<>(s, super.keySet());
437   }
438
439   @Override /* Overridden from OpenApiElement */
440   public Parameter strict() {
441      super.strict();
442      return this;
443   }
444
445   @Override /* Overridden from OpenApiElement */
446   public Parameter strict(Object value) {
447      super.strict(value);
448      return this;
449   }
450
451}