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.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023import static org.apache.juneau.internal.ConverterUtils.*;
024
025import java.util.*;
026
027import org.apache.juneau.commons.collections.*;
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   @Override /* Overridden from OpenApiElement */
128   public <T> T get(String property, Class<T> type) {
129      assertArgNotNull("property", property);
130      return switch (property) {
131         case "name" -> toType(getName(), type);
132         case "in" -> toType(getIn(), type);
133         case "description" -> toType(getDescription(), type);
134         case "required" -> toType(getRequired(), type);
135         case "deprecated" -> toType(getDeprecated(), type);
136         case "allowEmptyValue" -> toType(getAllowEmptyValue(), type);
137         case "style" -> toType(getStyle(), type);
138         case "explode" -> toType(getExplode(), type);
139         case "allowReserved" -> toType(getAllowReserved(), type);
140         case "schema" -> toType(getSchema(), type);
141         case "example" -> toType(getExample(), type);
142         case "examples" -> toType(getExamples(), type);
143         default -> super.get(property, type);
144      };
145   }
146
147   /**
148    * Returns the allow empty value flag.
149    *
150    * @return The allow empty value flag.
151    */
152   public Boolean getAllowEmptyValue() { return allowEmptyValue; }
153
154   /**
155    * Returns the allow reserved flag.
156    *
157    * @return The allow reserved flag.
158    */
159   public Boolean getAllowReserved() { return allowReserved; }
160
161   /**
162    * Returns the deprecated flag.
163    *
164    * @return The deprecated flag.
165    */
166   public Boolean getDeprecated() { return deprecated; }
167
168   /**
169    * Returns the description.
170    *
171    * @return The description.
172    */
173   public String getDescription() { return description; }
174
175   /**
176    * Returns the example.
177    *
178    * @return The example.
179    */
180   public Object getExample() { return example; }
181
182   /**
183    * Returns the examples map.
184    *
185    * @return The examples map.
186    */
187   public Map<String,Example> getExamples() { return examples; }
188
189   /**
190    * Returns the explode flag.
191    *
192    * @return The explode flag.
193    */
194   public Boolean getExplode() { return explode; }
195
196   /**
197    * Returns the parameter location.
198    *
199    * @return The parameter location.
200    */
201   public String getIn() { return in; }
202
203   /**
204    * Returns the parameter name.
205    *
206    * @return The parameter name.
207    */
208   public String getName() { return name; }
209
210   /**
211    * Returns the required flag.
212    *
213    * @return The required flag.
214    */
215   public Boolean getRequired() { return required; }
216
217   /**
218    * Returns the schema.
219    *
220    * @return The schema.
221    */
222   public SchemaInfo getSchema() { return schema; }
223
224   /**
225    * Returns the style.
226    *
227    * @return The style.
228    */
229   public String getStyle() { return style; }
230
231   @Override /* Overridden from OpenApiElement */
232   public Set<String> keySet() {
233      // @formatter:off
234      var s = setb(String.class)
235         .addIf(nn(allowEmptyValue), "allowEmptyValue")
236         .addIf(nn(allowReserved), "allowReserved")
237         .addIf(nn(deprecated), "deprecated")
238         .addIf(nn(description), "description")
239         .addIf(nn(example), "example")
240         .addIf(nn(examples), "examples")
241         .addIf(nn(explode), "explode")
242         .addIf(nn(in), "in")
243         .addIf(nn(name), "name")
244         .addIf(nn(required), "required")
245         .addIf(nn(schema), "schema")
246         .addIf(nn(style), "style")
247         .build();
248      // @formatter:on
249      return new MultiSet<>(s, super.keySet());
250   }
251
252   @Override /* Overridden from OpenApiElement */
253   public Parameter set(String property, Object value) {
254      assertArgNotNull("property", property);
255      return switch (property) {
256         case "allowEmptyValue" -> setAllowEmptyValue(toType(value, Boolean.class));
257         case "allowReserved" -> setAllowReserved(toType(value, Boolean.class));
258         case "description" -> setDescription(s(value));
259         case "deprecated" -> setDeprecated(toType(value, Boolean.class));
260         case "example" -> setExample(value);
261         case "examples" -> setExamples(toMapBuilder(value, String.class, Example.class).sparse().build());
262         case "explode" -> setExplode(toType(value, Boolean.class));
263         case "in" -> setIn(s(value));
264         case "name" -> setName(s(value));
265         case "required" -> setRequired(toType(value, Boolean.class));
266         case "schema" -> setSchema(toType(value, SchemaInfo.class));
267         case "style" -> setStyle(s(value));
268         default -> {
269            super.set(property, value);
270            yield this;
271         }
272      };
273   }
274
275   /**
276    * Sets the allow empty value flag.
277    *
278    * @param value The new value for this property.
279    * @return This object.
280    */
281   public Parameter setAllowEmptyValue(Boolean value) {
282      allowEmptyValue = value;
283      return this;
284   }
285
286   /**
287    * Sets the allow reserved flag.
288    *
289    * @param value The new value for this property.
290    * @return This object.
291    */
292   public Parameter setAllowReserved(Boolean value) {
293      allowReserved = value;
294      return this;
295   }
296
297   /**
298    * Sets the deprecated flag.
299    *
300    * @param value The new value for this property.
301    * @return This object.
302    */
303   public Parameter setDeprecated(Boolean value) {
304      deprecated = value;
305      return this;
306   }
307
308   /**
309    * Sets the description.
310    *
311    * @param value The new value for this property.
312    * @return This object.
313    */
314   public Parameter setDescription(String value) {
315      description = value;
316      return this;
317   }
318
319   /**
320    * Sets the example.
321    *
322    * @param value The new value for this property.
323    * @return This object.
324    */
325   public Parameter setExample(Object value) {
326      example = value;
327      return this;
328   }
329
330   /**
331    * Sets the examples map.
332    *
333    * @param value The new value for this property.
334    * @return This object.
335    */
336   public Parameter setExamples(Map<String,Example> value) {
337      examples = value;
338      return this;
339   }
340
341   /**
342    * Sets the explode flag.
343    *
344    * @param value The new value for this property.
345    * @return This object.
346    */
347   public Parameter setExplode(Boolean value) {
348      explode = value;
349      return this;
350   }
351
352   /**
353    * Sets the parameter location.
354    *
355    * @param value The new value for this property.
356    * @return This object.
357    */
358   public Parameter setIn(String value) {
359      if (isStrict() && ! contains(value, VALID_IN))
360         throw rex("Invalid value passed in to setIn(String).  Value=''{0}'', valid values={1}", value, Json5.of(VALID_IN));
361      in = value;
362      return this;
363   }
364
365   /**
366    * Sets the parameter name.
367    *
368    * @param value The new value for this property.
369    * @return This object.
370    */
371   public Parameter setName(String value) {
372      name = value;
373      return this;
374   }
375
376   /**
377    * Sets the required flag.
378    *
379    * @param value The new value for this property.
380    * @return This object.
381    */
382   public Parameter setRequired(Boolean value) {
383      required = value;
384      return this;
385   }
386
387   /**
388    * Sets the schema.
389    *
390    * @param value The new value for this property.
391    * @return This object.
392    */
393   public Parameter setSchema(SchemaInfo value) {
394      schema = value;
395      return this;
396   }
397
398   /**
399    * Sets the style.
400    *
401    * @param value The new value for this property.
402    * @return This object.
403    */
404   public Parameter setStyle(String value) {
405      if (isStrict() && ! contains(value, VALID_STYLES))
406         throw rex("Invalid value passed in to setStyle(String).  Value=''{0}'', valid values={1}", value, Json5.of(VALID_STYLES));
407      style = value;
408      return this;
409   }
410
411   @Override /* Overridden from OpenApiElement */
412   public Parameter strict() {
413      super.strict();
414      return this;
415   }
416
417   @Override /* Overridden from OpenApiElement */
418   public Parameter strict(Object value) {
419      super.strict(value);
420      return this;
421   }
422}