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