001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http.header;
014
015import static org.apache.juneau.common.internal.StringUtils.*;
016
017import java.util.function.*;
018
019import org.apache.juneau.*;
020import org.apache.juneau.httppart.*;
021import org.apache.juneau.oapi.*;
022import org.apache.juneau.serializer.*;
023
024/**
025 * TODO
026 *
027 * <h5 class='section'>See Also:</h5><ul>
028 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
029 * </ul>
030 *
031 * @serial exclude
032 */
033public class SerializedHeader extends BasicHeader {
034
035   //-----------------------------------------------------------------------------------------------------------------
036   // Static
037   //-----------------------------------------------------------------------------------------------------------------
038
039   private static final long serialVersionUID = 1L;
040
041   /**
042    * Static creator.
043    *
044    * @param name The header name.
045    * @param value
046    *    The POJO to serialize as the header value.
047    *    <br>Can be <jk>null</jk>.
048    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
049    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
050    */
051   public static SerializedHeader of(String name, Object value) {
052      return new SerializedHeader(name, value, null, null, false);
053   }
054
055   /**
056    * Static creator with delayed value.
057    *
058    * <p>
059    * Header value is re-evaluated on each call to {@link #getValue()}.
060    *
061    * @param name The header name.
062    * @param value
063    *    The supplier of the POJO to serialize as the header value.
064    *    <br>Can be <jk>null</jk>.
065    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
066    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
067    */
068   public static SerializedHeader of(String name, Supplier<?> value) {
069      return new SerializedHeader(name, value, null, null, false);
070   }
071
072   /**
073    * Static creator.
074    *
075    * @param name The HTTP header name name.
076    * @param value
077    *    The POJO to serialize as the header value.
078    * @param serializer
079    *    The serializer to use for serializing the value to a string value.
080    * @param schema
081    *    The schema object that defines the format of the output.
082    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
083    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
084    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
085    *    <br>Can also be a {@link Supplier}.
086    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
087    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
088    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
089    */
090   public static SerializedHeader of(String name, Object value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
091      return new SerializedHeader(name, value, serializer, schema, skipIfEmpty);
092   }
093
094   /**
095    * Static creator with delayed value.
096    *
097    * <p>
098    * Header value is re-evaluated on each call to {@link #getValue()}.
099    *
100    * @param name The HTTP header name name.
101    * @param value
102    *    The supplier of the POJO to serialize as the header value.
103    * @param serializer
104    *    The serializer to use for serializing the value to a string value.
105    * @param schema
106    *    The schema object that defines the format of the output.
107    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
108    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
109    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
110    *    <br>Can also be a {@link Supplier}.
111    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
112    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
113    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
114    */
115   public static SerializedHeader of(String name, Supplier<?> value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
116      return new SerializedHeader(name, value, serializer, schema, skipIfEmpty);
117   }
118
119   //-----------------------------------------------------------------------------------------------------------------
120   // Instance
121   //-----------------------------------------------------------------------------------------------------------------
122
123   private final Object value;
124   private final Supplier<Object> supplier;
125   private HttpPartSerializerSession serializer;
126   private HttpPartSchema schema = HttpPartSchema.DEFAULT;
127   private boolean skipIfEmpty;
128
129   /**
130    * Constructor.
131    *
132    * @param name The HTTP header name name.
133    * @param value The POJO to serialize to the parameter value.
134    * @param serializer
135    *    The serializer to use for serializing the value to a string value.
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("unchecked")
146   public SerializedHeader(String name, Object value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
147      super(name, null);
148      this.value = value instanceof Supplier ? null : value;
149      this.supplier = value instanceof Supplier ? (Supplier<Object>)value : null;
150      this.serializer = serializer;
151      this.schema = schema;
152      this.skipIfEmpty = skipIfEmpty;
153   }
154
155   /**
156    * Constructor with delayed value.
157    *
158    * <p>
159    * Header value is re-evaluated on each call to {@link #getValue()}.
160    *
161    * @param name The HTTP header name name.
162    * @param value The supplier of the POJO to serialize to the parameter value.
163    * @param serializer
164    *    The serializer to use for serializing the value to a string value.
165    * @param schema
166    *    The schema object that defines the format of the output.
167    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
168    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
169    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
170    *    <br>Can also be a {@link Supplier}.
171    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
172    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
173    */
174   public SerializedHeader(String name, Supplier<Object> value, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
175      super(name, null);
176      this.value = null;
177      this.supplier = value;
178      this.serializer = serializer;
179      this.schema = schema;
180      this.skipIfEmpty = skipIfEmpty;
181   }
182
183   /**
184    * Copy constructor.
185    *
186    * @param copyFrom The object to copy.
187    */
188   protected SerializedHeader(SerializedHeader copyFrom) {
189      super(copyFrom);
190      this.value = copyFrom.value;
191      this.supplier = copyFrom.supplier;
192      this.serializer = copyFrom.serializer == null ? serializer : copyFrom.serializer;
193      this.schema = copyFrom.schema == null ? schema : copyFrom.schema;
194      this.skipIfEmpty = copyFrom.skipIfEmpty;
195   }
196
197   /**
198    * Creates a copy of this object.
199    *
200    * @return A new copy of this object.
201    */
202   public SerializedHeader copy() {
203      return new SerializedHeader(this);
204   }
205
206   /**
207    * Sets the serializer to use for serializing the value to a string value.
208    *
209    * @param value The new value for this property.
210    * @return This object.
211    */
212   public SerializedHeader serializer(HttpPartSerializer value) {
213      if (value != null)
214         return serializer(value.getPartSession());
215      return this;
216   }
217
218   /**
219    * Sets the serializer to use for serializing the value to a string value.
220    *
221    * @param value The new value for this property.
222    * @return This object.
223    */
224   public SerializedHeader serializer(HttpPartSerializerSession value) {
225      serializer = value;
226      return this;
227   }
228
229   /**
230    * Sets the schema object that defines the format of the output.
231    *
232    * @param value The new value for this property.
233    * @return This object.
234    */
235   public SerializedHeader schema(HttpPartSchema value) {
236      this.schema = value;
237      return this;
238   }
239
240   /**
241    * Copies this bean and sets the serializer and schema on it.
242    *
243    * @param serializer The new serializer for the bean.  Can be <jk>null</jk>.
244    * @param schema The new schema for the bean.  Can be <jk>null</jk>.
245    * @return Either a new bean with the serializer set, or this bean if
246    *    both values are <jk>null</jk> or the serializer and schema were already set.
247    */
248   public SerializedHeader copyWith(HttpPartSerializerSession serializer, HttpPartSchema schema) {
249      if ((this.serializer == null && serializer != null) || (this.schema == null && schema != null)) {
250         SerializedHeader h = copy();
251         if (serializer != null)
252            h.serializer(serializer);
253         if (schema != null)
254            h.schema(schema);
255         return h;
256      }
257      return this;
258   }
259
260   /**
261    * Don't serialize this header if the value is <jk>null</jk> or an empty string.
262    *
263    * @return This object.
264    */
265   public SerializedHeader skipIfEmpty() {
266      return skipIfEmpty(true);
267   }
268
269   /**
270    * Don't serialize this header if the value is <jk>null</jk> or an empty string.
271    *
272    * @param value The new value of this setting.
273    * @return This object.
274    */
275   public SerializedHeader skipIfEmpty(boolean value) {
276      this.skipIfEmpty = value;
277      return this;
278   }
279
280   @Override /* NameValuePair */
281   public String getValue() {
282      try {
283         Object v = value;
284         if (supplier != null)
285            v = supplier.get();
286         HttpPartSchema schema = this.schema == null ? HttpPartSchema.DEFAULT : this.schema;
287         String def = schema.getDefault();
288         if (v == null) {
289            if ((def == null && ! schema.isRequired()) || (def == null && schema.isAllowEmptyValue()))
290               return null;
291         }
292         if (isEmpty(stringify(v)) && skipIfEmpty && def == null)
293            return null;
294         return serializer == null ? stringify(v) : serializer.serialize(HttpPartType.HEADER, schema, v);
295      } catch (SchemaValidationException e) {
296         throw new BasicRuntimeException(e, "Validation error on request {0} parameter ''{1}''=''{2}''", HttpPartType.HEADER, getName(), value);
297      } catch (SerializeException e) {
298         throw new BasicRuntimeException(e, "Serialization error on request {0} parameter ''{1}''", HttpPartType.HEADER, getName());
299      }
300   }
301}