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.commons.utils.Shorts.*;
021import static org.apache.juneau.marshall.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.commons.collections.*;
026
027/**
028 * A single encoding definition applied to a single schema property.
029 *
030 * <p>
031 * The Encoding Object is a single encoding definition applied to a single schema property. It allows you to define
032 * how a property should be serialized when it's part of a request or response body with a specific media type.
033 *
034 * <h5 class='section'>OpenAPI Specification:</h5>
035 * <p>
036 * The Encoding Object is composed of the following fields:
037 * <ul class='spaced-list'>
038 *    <li><c>contentType</c> (string) - The Content-Type for encoding a specific property. Default value depends on the property type
039 *    <li><c>headers</c> (map of {@link HeaderInfo}) - A map allowing additional information to be provided as headers
040 *    <li><c>style</c> (string) - Describes how a specific property value will be serialized depending on its type
041 *    <li><c>explode</c> (boolean) - When this is true, property values of type array or object generate separate parameters for each value
042 *    <li><c>allowReserved</c> (boolean) - Determines whether the parameter value should allow reserved characters
043 * </ul>
044 *
045 * <h5 class='section'>Example:</h5>
046 * <p class='bcode'>
047 *    <jc>// Construct using SwaggerBuilder.</jc>
048 *    Encoding <jv>x</jv> = <jsm>encoding</jsm>()
049 *       .setContentType(<js>"application/x-www-form-urlencoded"</js>)
050 *       .setStyle(<js>"form"</js>)
051 *       .setExplode(<jk>true</jk>);
052 *
053 *    <jc>// Serialize using JsonSerializer.</jc>
054 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
055 *
056 *    <jc>// Or just use toString() which does the same as above.</jc>
057 *    <jv>json</jv> = <jv>x</jv>.toString();
058 * </p>
059 * <p class='bcode'>
060 *    <jc>// Output</jc>
061 *    {
062 *       <js>"contentType"</js>: <js>"application/x-www-form-urlencoded"</js>,
063 *       <js>"style"</js>: <js>"form"</js>,
064 *       <js>"explode"</js>: <jk>true</jk>
065 *    }
066 * </p>
067 *
068 * <h5 class='section'>See Also:</h5><ul>
069 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#encoding-object">OpenAPI Specification &gt; Encoding Object</a>
070 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-request-body/">OpenAPI Describing Request Body</a>
071 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
072 * </ul>
073 */
074@SuppressWarnings({
075   "java:S1192" // Duplicated literals (property names) read more clearly inline than as constants
076})
077public class Encoding extends OpenApiElement {
078
079   private String contentType;
080   private String style;
081   private final Map<String,HeaderInfo> headers = map();
082   private Boolean explode;
083   private Boolean allowReserved;
084
085   /**
086    * Default constructor.
087    */
088   public Encoding() {}
089
090   /**
091    * Copy constructor.
092    *
093    * @param copyFrom The object to copy.
094    */
095   public Encoding(Encoding copyFrom) {
096      super(copyFrom);
097
098      this.contentType = copyFrom.contentType;
099      this.style = copyFrom.style;
100      this.explode = copyFrom.explode;
101      this.allowReserved = copyFrom.allowReserved;
102      headers.putAll(cp(copyFrom.headers, HeaderInfo::copy));
103   }
104
105   /**
106    * Adds one or more values to the <property>headers</property> property.
107    *
108    * @param key The mapping key.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
109    * @param value
110    *    The values to add to this property.
111    *    <br>Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
112    * @return This object
113    */
114   public Encoding addHeader(String key, HeaderInfo value) {
115      reqnn("key", key);
116      reqnn("value", value);
117      headers.put(key, value);
118      return this;
119   }
120
121   /**
122    * Make a deep copy of this object.
123    *
124    * @return A deep copy of this object.
125    */
126   public Encoding copy() {
127      return new Encoding(this);
128   }
129
130   @Override /* Overridden from OpenApiElement */
131   public <T> T get(String property, Class<T> type) {
132      reqnn("property", property);
133      return switch (property) {
134         case "contentType" -> toType(getContentType(), type);
135         case "style" -> toType(getStyle(), type);
136         case "headers" -> toType(getHeaders(), type);
137         case "explode" -> toType(getExplode(), type);
138         case "allowReserved" -> toType(getAllowReserved(), type);
139         default -> super.get(property, type);
140      };
141   }
142
143   /**
144    * Bean property getter:  <property>allowReserved</property>.
145    *
146    * <p>
147    * Determines whether the parameter value should allow reserved characters.
148    *
149    * @return The property value, or <jk>null</jk> if it is not set.
150    */
151   public Boolean getAllowReserved() { return allowReserved; }
152
153   /**
154    * Bean property getter:  <property>contentType</property>.
155    *
156    * <p>
157    * The Content-Type for encoding a specific property.
158    *
159    * @return The property value, or <jk>null</jk> if it is not set.
160    */
161   public String getContentType() { return contentType; }
162
163   /**
164    * Bean property getter:  <property>explode</property>.
165    *
166    * <p>
167    * When this is true, property values of type array or object generate separate parameters for each value.
168    *
169    * @return The property value, or <jk>null</jk> if it is not set.
170    */
171   public Boolean getExplode() { return explode; }
172
173   /**
174    * Bean property getter:  <property>headers</property>.
175    *
176    * @return The property value, or <jk>null</jk> if it is not set.
177    */
178   public Map<String,HeaderInfo> getHeaders() { return nie(headers); }
179
180   /**
181    * Bean property getter:  <property>style</property>.
182    *
183    * @return The property value, or <jk>null</jk> if it is not set.
184    */
185   public String getStyle() { return style; }
186
187   @Override /* Overridden from OpenApiElement */
188   public Set<String> keySet() {
189      // @formatter:off
190      var s = stb(String.class)
191         .addIf(nn(allowReserved), "allowReserved")
192         .addIf(nn(contentType), "contentType")
193         .addIf(nn(explode), "explode")
194         .addIf(ine(headers), "headers")
195         .addIf(nn(style), "style")
196         .build();
197      // @formatter:on
198      return new MultiSet<>(s, super.keySet());
199   }
200
201   @Override /* Overridden from OpenApiElement */
202   public Encoding set(String property, Object value) {
203      reqnn("property", property);
204      return switch (property) {
205         case "allowReserved" -> setAllowReserved(toBoolean(value));
206         case "contentType" -> setContentType(s(value));
207         case "explode" -> setExplode(toBoolean(value));
208         case "headers" -> setHeaders(toMapBuilder(value, String.class, HeaderInfo.class).sparse().build());
209         case "style" -> setStyle(s(value));
210         default -> {
211            super.set(property, value);
212            yield this;
213         }
214      };
215   }
216
217   /**
218    * Bean property setter:  <property>allowReserved</property>.
219    *
220    * <p>
221    * Determines whether the parameter value should allow reserved characters.
222    *
223    * @param value
224    *    The new value for this property.
225    *    <br>Can be <jk>null</jk> to unset the property.
226    * @return This object
227    */
228   public Encoding setAllowReserved(Boolean value) {
229      allowReserved = value;
230      return this;
231   }
232
233   /**
234    * Bean property setter:  <property>contentType</property>.
235    *
236    * <p>
237    * The Content-Type for encoding a specific property.
238    *
239    * @param value
240    *    The new value for this property.
241    *    <br>Can be <jk>null</jk> to unset the property.
242    * @return This object
243    */
244   public Encoding setContentType(String value) {
245      contentType = value;
246      return this;
247   }
248
249   /**
250    * Bean property setter:  <property>explode</property>.
251    *
252    * <p>
253    * When this is true, property values of type array or object generate separate parameters for each value.
254    *
255    * @param value
256    *    The new value for this property.
257    *    <br>Can be <jk>null</jk> to unset the property.
258    * @return This object
259    */
260   public Encoding setExplode(Boolean value) {
261      explode = value;
262      return this;
263   }
264
265   /**
266    * Bean property setter:  <property>headers</property>.
267    *
268    * @param value
269    *    The new value for this property.
270    *    <br>Can be <jk>null</jk> to unset the property.
271    * @return This object
272    */
273   public Encoding setHeaders(Map<String,HeaderInfo> value) {
274      headers.clear();
275      if (nn(value))
276         headers.putAll(value);
277      return this;
278   }
279
280   /**
281    * Bean property setter:  <property>style</property>.
282    *
283    * @param value
284    *    The new value for this property.
285    *    <br>Can be <jk>null</jk> to unset the property.
286    * @return This object
287    */
288   public Encoding setStyle(String value) {
289      style = value;
290      return this;
291   }
292
293   @Override /* Overridden from OpenApiElement */
294   public Encoding strict(Object value) {
295      super.strict(value);
296      return this;
297   }
298
299   @Override /* Overridden from OpenApiElement */
300   protected Encoding strict() {
301      super.strict();
302      return this;
303   }
304}