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.http.classic.header;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.function.*;
022
023import org.apache.juneau.commons.httppart.*;
024import org.apache.juneau.marshall.httppart.*;
025import org.apache.juneau.marshall.oapi.*;
026import org.apache.juneau.marshall.serializer.*;
027
028/**
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
031 * </ul>
032 *
033 * @serial exclude
034 */
035@SuppressWarnings({
036   "java:S2160", // equals() inherited from BasicHeader compares name+value; serializer field affects rendering not identity
037   "java:S9149" // Public Juneau DSL factory; hiding parent of()/create() is intentional.
038})
039public class SerializedHeader extends BasicHeader {
040   private static final long serialVersionUID = 1L;
041
042   /**
043    * Static creator.
044    *
045    * @param name The header name.
046    * @param value
047    *    The POJO to serialize as the header value.
048    *    <br>Can be <jk>null</jk>.  The effect is implementation-dependent — see {@link HttpPartSerializerSession} and {@link HttpPartSchema}.
049    * @return A new header bean.  Never <jk>null</jk>.
050    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
051    */
052   public static SerializedHeader of(String name, Object value) {
053      return new SerializedHeader(name, value, null, null, false);
054   }
055
056   /**
057    * Static creator.
058    *
059    * @param name The HTTP header name name.
060    * @param value
061    *    The POJO to serialize as the header value.
062    * @param serializer
063    *    The serializer to use for serializing the value to a string value.
064    *    <br>Can be <jk>null</jk>, in which case the value is converted directly to a string instead of being serialized.
065    * @param schema
066    *    The schema object that defines the format of the output.
067    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
068    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
069    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
070    *    <br>Can also be a {@link Supplier}.
071    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
072    * @return A new header bean.  Never <jk>null</jk>.
073    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
074    */
075   public static SerializedHeader of(String name, Object value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
076      return new SerializedHeader(name, value, serializer, schema, skipIfEmpty);
077   }
078
079   /**
080    * Static creator with delayed value.
081    *
082    * <p>
083    * Header value is re-evaluated on each call to {@link #getValue()}.
084    *
085    * @param name The header name.
086    * @param value
087    *    The supplier of the POJO to serialize as the header value.
088    *    <br>Can be <jk>null</jk>.  The effect is implementation-dependent — see {@link HttpPartSerializerSession} and {@link HttpPartSchema}.
089    * @return A new header bean.  Never <jk>null</jk>.
090    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
091    */
092   public static SerializedHeader of(String name, Supplier<?> value) {
093      return new SerializedHeader(name, value, null, null, false);
094   }
095
096   /**
097    * Static creator with delayed value.
098    *
099    * <p>
100    * Header value is re-evaluated on each call to {@link #getValue()}.
101    *
102    * @param name The HTTP header name name.
103    * @param value
104    *    The supplier of the POJO to serialize as the header value.
105    * @param serializer
106    *    The serializer to use for serializing the value to a string value.
107    *    <br>Can be <jk>null</jk>, in which case the value is converted directly to a string instead of being serialized.
108    * @param schema
109    *    The schema object that defines the format of the output.
110    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
111    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
112    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
113    *    <br>Can also be a {@link Supplier}.
114    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
115    * @return A new header bean.  Never <jk>null</jk>.
116    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
117    */
118   public static SerializedHeader of(String name, Supplier<?> value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
119      return new SerializedHeader(name, value, serializer, schema, skipIfEmpty);
120   }
121
122   private final transient Object value;
123   private final transient Supplier<Object> supplier;
124   private transient HttpPartSerializerSession serializer;
125   private transient HttpPartSchema schema = HttpPartSchema.DEFAULT;
126   private boolean skipIfEmpty;
127
128   /**
129    * Constructor.
130    *
131    * @param name The HTTP header name name.
132    * @param value The POJO to serialize to the parameter value.  Can be <jk>null</jk>.  The effect is implementation-dependent — see {@link HttpPartSerializerSession} and {@link HttpPartSchema}.
133    * @param serializer
134    *    The serializer to use for serializing the value to a string value.
135    *    <br>Can be <jk>null</jk>, in which case the value is converted directly to a string instead of being serialized.
136    * @param schema
137    *    The schema object that defines the format of the output.
138    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
139    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
140    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
141    *    <br>Can also be a {@link Supplier}.
142    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
143    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
144    */
145   @SuppressWarnings({
146      "unchecked" // Type erasure requires cast for supplier
147   })
148   public SerializedHeader(String name, Object value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
149      super(name, null);
150      this.value = value instanceof Supplier ? null : value;
151      this.supplier = value instanceof Supplier ? (Supplier<Object>)value : null;
152      this.serializer = serializer;
153      this.schema = schema;
154      this.skipIfEmpty = skipIfEmpty;
155   }
156
157   /**
158    * Constructor with delayed value.
159    *
160    * <p>
161    * Header value is re-evaluated on each call to {@link #getValue()}.
162    *
163    * @param name The HTTP header name name.
164    * @param value The supplier of the POJO to serialize to the parameter value.  Can be <jk>null</jk>.  The effect is implementation-dependent — see {@link HttpPartSerializerSession} and {@link HttpPartSchema}.
165    * @param serializer
166    *    The serializer to use for serializing the value to a string value.
167    *    <br>Can be <jk>null</jk>, in which case the value is converted directly to a string instead of being serialized.
168    * @param schema
169    *    The schema object that defines the format of the output.
170    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
171    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
172    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
173    *    <br>Can also be a {@link Supplier}.
174    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
175    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
176    */
177   public SerializedHeader(String name, Supplier<Object> value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
178      super(name, null);
179      this.value = null;
180      supplier = value;
181      this.serializer = serializer;
182      this.schema = schema;
183      this.skipIfEmpty = skipIfEmpty;
184   }
185
186   /**
187    * Copy constructor.
188    *
189    * @param copyFrom The object to copy.  Must not be <jk>null</jk> or a {@link NullPointerException} is thrown.
190    */
191   protected SerializedHeader(SerializedHeader copyFrom) {
192      super(copyFrom);
193      this.value = copyFrom.value;
194      this.supplier = copyFrom.supplier;
195      this.serializer = copyFrom.serializer == null ? serializer : copyFrom.serializer;
196      this.schema = copyFrom.schema == null ? schema : copyFrom.schema;
197      this.skipIfEmpty = copyFrom.skipIfEmpty;
198   }
199
200   /**
201    * Creates a copy of this object.
202    *
203    * @return A new copy of this object.
204    */
205   @Override /* Overridden from BasicHeader */
206   public SerializedHeader copy() {
207      return new SerializedHeader(this);
208   }
209
210   /**
211    * Copies this bean and sets the serializer and schema on it.
212    *
213    * @param serializer The new serializer for the bean.  Can be <jk>null</jk> to leave the serializer unchanged.
214    * @param schema The new schema for the bean.  Can be <jk>null</jk> to leave the schema unchanged.
215    * @return Either a new bean with the serializer set, or this bean if
216    *    both values are <jk>null</jk> or the serializer and schema were already set.
217    */
218   public SerializedHeader copyWith(HttpPartSerializerSession serializer, HttpPartSchema schema) {
219      if ((this.serializer == null && nn(serializer)) || (this.schema == null && nn(schema))) {
220         SerializedHeader h = copy();
221         if (nn(serializer))
222            h.serializer(serializer);
223         if (nn(schema))
224            h.schema(schema);
225         return h;
226      }
227      return this;
228   }
229
230   @Override /* Overridden from NameValuePair */
231   public String getValue() {
232      try {
233         Object v = value;
234         if (nn(supplier))
235            v = supplier.get();
236         HttpPartSchema schema2 = this.schema == null ? HttpPartSchema.DEFAULT : this.schema;
237         var def = schema2.getDefault();
238         if (v == null && ((def == null && ! schema2.isRequired()) || (def == null && schema2.isAllowEmptyValue())))
239            return null;
240         if (ie(s(v)) && skipIfEmpty && def == null)
241            return null;
242         return serializer == null ? s(v) : serializer.write(HttpPartType.HEADER, schema2, v);
243      } catch (SchemaValidationException e) {
244         throw rex(e, "Validation error on request %s parameter '%s'='%s'", HttpPartType.HEADER, getName(), value);
245      } catch (SerializeException e) {
246         throw rex(e, "Serialization error on request %s parameter '%s'", HttpPartType.HEADER, getName());
247      }
248   }
249
250   /**
251    * Sets the schema object that defines the format of the output.
252    *
253    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
254    * @return This object.
255    */
256   public SerializedHeader schema(HttpPartSchema value) {
257      schema = value;
258      return this;
259   }
260
261   /**
262    * Sets the serializer to use for serializing the value to a string value.
263    *
264    * @param value The new value for this property.  Can be <jk>null</jk> (no-op).
265    * @return This object.
266    */
267   public SerializedHeader serializer(HttpPartSerializer value) {
268      if (nn(value))
269         return serializer(value.getPartSession());
270      return this;
271   }
272
273   /**
274    * Sets the serializer to use for serializing the value to a string value.
275    *
276    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
277    * @return This object.
278    */
279   public SerializedHeader serializer(HttpPartSerializerSession value) {
280      serializer = value;
281      return this;
282   }
283
284   /**
285    * Don't serialize this header if the value is <jk>null</jk> or an empty string.
286    *
287    * @return This object.
288    */
289   public SerializedHeader skipIfEmpty() {
290      return skipIfEmpty(true);
291   }
292
293   /**
294    * Don't serialize this header if the value is <jk>null</jk> or an empty string.
295    *
296    * @param value The new value of this setting.
297    * @return This object.
298    */
299   public SerializedHeader skipIfEmpty(boolean value) {
300      skipIfEmpty = value;
301      return this;
302   }
303}