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