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.Utils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.net.*;
025import java.util.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.commons.collections.*;
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, style;
080   private Map<String,HeaderInfo> headers = map();
081   private Boolean explode, allowReserved;
082
083   /**
084    * Default constructor.
085    */
086   public Encoding() {}
087
088   /**
089    * Copy constructor.
090    *
091    * @param copyFrom The object to copy.
092    */
093   public Encoding(Encoding copyFrom) {
094      super(copyFrom);
095
096      this.contentType = copyFrom.contentType;
097      this.style = copyFrom.style;
098      this.explode = copyFrom.explode;
099      this.allowReserved = copyFrom.allowReserved;
100      if (nn(copyFrom.headers))
101         headers.putAll(copyOf(copyFrom.headers, HeaderInfo::copy));
102   }
103
104   /**
105    * Adds one or more values to the <property>headers</property> property.
106    *
107    * @param key The mapping key.  Must not be <jk>null</jk>.
108    * @param value
109    *    The values to add to this property.
110    *    <br>Must not be <jk>null</jk>.
111    * @return This object
112    */
113   public Encoding addHeader(String key, HeaderInfo value) {
114      assertArgNotNull("key", key);
115      assertArgNotNull("value", value);
116      headers.put(key, value);
117      return this;
118   }
119
120   /**
121    * Make a deep copy of this object.
122    *
123    * @return A deep copy of this object.
124    */
125   public Encoding copy() {
126      return new Encoding(this);
127   }
128
129   @Override /* Overridden from OpenApiElement */
130   public <T> T get(String property, Class<T> type) {
131      assertArgNotNull("property", property);
132      return switch (property) {
133         case "contentType" -> toType(getContentType(), type);
134         case "style" -> toType(getStyle(), type);
135         case "headers" -> toType(getHeaders(), type);
136         case "explode" -> toType(getExplode(), type);
137         case "allowReserved" -> toType(getAllowReserved(), type);
138         default -> super.get(property, type);
139      };
140   }
141
142   /**
143    * Bean property getter:  <property>required</property>.
144    *
145    * <p>
146    * The type of the object.
147    *
148    * @return The property value, or <jk>null</jk> if it is not set.
149    */
150   public Boolean getAllowReserved() { return allowReserved; }
151
152   /**
153    * Bean property getter:  <property>contentType</property>.
154    *
155    * <p>
156    * The URL pointing to the contact information.
157    *
158    * @return The property value, or <jk>null</jk> if it is not set.
159    */
160   public String getContentType() { return contentType; }
161
162   /**
163    * Bean property getter:  <property>required</property>.
164    *
165    * <p>
166    * The type of the object.
167    *
168    * @return The property value, or <jk>null</jk> if it is not set.
169    */
170   public Boolean getExplode() { return explode; }
171
172   /**
173    * Bean property getter:  <property>variables</property>.
174    *
175    * @return The property value, or <jk>null</jk> if it is not set.
176    */
177   public Map<String,HeaderInfo> getHeaders() { return nullIfEmpty(headers); }
178
179   /**
180    * Bean property getter:  <property>style</property>.
181    *
182    * @return The property value, or <jk>null</jk> if it is not set.
183    */
184   public String getStyle() { return style; }
185
186   @Override /* Overridden from OpenApiElement */
187   public Set<String> keySet() {
188      // @formatter:off
189      var s = setb(String.class)
190         .addIf(nn(allowReserved), "allowReserved")
191         .addIf(nn(contentType), "contentType")
192         .addIf(nn(explode), "explode")
193         .addIf(ne(headers), "headers")
194         .addIf(nn(style), "style")
195         .build();
196      // @formatter:on
197      return new MultiSet<>(s, super.keySet());
198   }
199
200   @Override /* Overridden from OpenApiElement */
201   public Encoding set(String property, Object value) {
202      assertArgNotNull("property", property);
203      return switch (property) {
204         case "allowReserved" -> setAllowReserved(toBoolean(value));
205         case "contentType" -> setContentType(s(value));
206         case "explode" -> setExplode(toBoolean(value));
207         case "headers" -> setHeaders(toMapBuilder(value, String.class, HeaderInfo.class).sparse().build());
208         case "style" -> setStyle(s(value));
209         default -> {
210            super.set(property, value);
211            yield this;
212         }
213      };
214   }
215
216   /**
217    * Bean property setter:  <property>explode</property>.
218    *
219    * <p>
220    * The type of the object.
221    *
222    * @param value
223    *    The new value for this property.
224    *    <br>Property value is required.
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>url</property>.
235    *
236    * <p>
237    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
238    * <br>Strings must be valid URIs.
239    *
240    * <p>
241    * URIs defined by {@link UriResolver} can be used for values.
242    *
243    * @param value
244    *    The new value for this property.
245    *    <br>Can be <jk>null</jk> to unset the property.
246    * @return This object
247    */
248   public Encoding setContentType(String value) {
249      contentType = value;
250      return this;
251   }
252
253   /**
254    * Bean property setter:  <property>explode</property>.
255    *
256    * <p>
257    * The type of the object.
258    *
259    * @param value
260    *    The new value for this property.
261    *    <br>Property value is required.
262    *    <br>Can be <jk>null</jk> to unset the property.
263    * @return This object
264    */
265   public Encoding setExplode(Boolean value) {
266      explode = value;
267      return this;
268   }
269
270   /**
271    * Bean property setter:  <property>variables</property>.
272    *
273    * @param value
274    *    The new value for this property.
275    *    <br>Can be <jk>null</jk> to unset the property.
276    * @return This object
277    */
278   public Encoding setHeaders(Map<String,HeaderInfo> value) {
279      headers.clear();
280      if (nn(value))
281         headers.putAll(value);
282      return this;
283   }
284
285   /**
286    * Bean property setter:  <property>description</property>.
287    *
288    * @param value
289    *    The new value for this property.
290    *    <br>Can be <jk>null</jk> to unset the property.
291    * @return This object
292    */
293   public Encoding setStyle(String value) {
294      style = value;
295      return this;
296   }
297
298   @Override /* Overridden from OpenApiElement */
299   public Encoding strict(Object value) {
300      super.strict(value);
301      return this;
302   }
303
304   @Override /* Overridden from OpenApiElement */
305   protected Encoding strict() {
306      super.strict();
307      return this;
308   }
309}