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.part;
014
015import static org.apache.juneau.common.internal.StringUtils.*;
016
017import java.util.function.*;
018
019import org.apache.http.*;
020import org.apache.juneau.*;
021import org.apache.juneau.http.header.*;
022import org.apache.juneau.httppart.*;
023import org.apache.juneau.oapi.*;
024import org.apache.juneau.serializer.*;
025import org.apache.juneau.urlencoding.*;
026
027/**
028 * Subclass of {@link NameValuePair} for serializing POJOs as URL-encoded form post entries using the
029 * {@link UrlEncodingSerializer class}.
030 *
031 * <h5 class='section'>Example:</h5>
032 * <p class='bjava'>
033 *    NameValuePairs <jv>params</jv> = <jk>new</jk> NameValuePairs()
034 *       .append(<jk>new</jk> SerializedNameValuePair(<js>"myPojo"</js>, <jv>pojo</jv>, UrlEncodingSerializer.<jsf>DEFAULT_SIMPLE</jsf>))
035 *       .append(<jk>new</jk> BasicNameValuePair(<js>"someOtherParam"</js>, <js>"foobar"</js>));
036 *    <jv>request</jv>.setEntity(<jk>new</jk> UrlEncodedFormEntity(<jv>params</jv>));
037 * </p>
038 *
039 * <h5 class='section'>See Also:</h5><ul>
040 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
041 * </ul>
042 */
043public class SerializedPart extends BasicPart implements Headerable {
044   private final Object value;
045   private HttpPartType type;
046   private HttpPartSerializerSession serializer;
047   private HttpPartSchema schema = HttpPartSchema.DEFAULT;
048   private boolean skipIfEmpty;
049
050   /**
051    * Instantiates a new instance of this object.
052    *
053    * @param name The part name.
054    * @param value
055    *    The part value.
056    *    <br>Can be any POJO.
057    * @return A new {@link SerializedPart} object, never <jk>null</jk>.
058    */
059   public static SerializedPart of(String name, Object value) {
060      return new SerializedPart(name, value, null, null, null, false);
061   }
062
063   /**
064    * Instantiates a new instance of this object.
065    *
066    * @param name The part name.
067    * @param value
068    *    The part value supplier.
069    *    <br>Can be a supplier of any POJO.
070    * @return A new {@link SerializedPart} object, never <jk>null</jk>.
071    */
072   public static SerializedPart of(String name, Supplier<?> value) {
073      return new SerializedPart(name, value, null, null, null, false);
074   }
075
076   /**
077    * Constructor.
078    *
079    * @param name The part name.
080    * @param value The POJO to serialize to The part value.
081    * @param type The HTTP part type.
082    * @param serializer
083    *    The serializer to use for serializing the value to a string value.
084    * @param schema
085    *    The schema object that defines the format of the output.
086    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
087    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
088    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
089    * @param skipIfEmpty If value is a blank string, the value should return as <jk>null</jk>.
090    */
091   public SerializedPart(String name, Object value, HttpPartType type, HttpPartSerializerSession serializer, HttpPartSchema schema, boolean skipIfEmpty) {
092      super(name, value);
093      this.value = value;
094      this.type = type;
095      this.serializer = serializer;
096      this.schema = schema;
097      this.skipIfEmpty = skipIfEmpty;
098   }
099
100   /**
101    * Copy constructor.
102    *
103    * @param copyFrom The object to copy.
104    */
105   protected SerializedPart(SerializedPart copyFrom) {
106      super(copyFrom);
107      this.value = copyFrom.value;
108      this.type = copyFrom.type;
109      this.serializer = copyFrom.serializer == null ? serializer : copyFrom.serializer;
110      this.schema = copyFrom.schema == null ? schema : copyFrom.schema;
111      this.skipIfEmpty = copyFrom.skipIfEmpty;
112   }
113
114   /**
115    * Creates a copy of this object.
116    *
117    * @return A new copy of this object.
118    */
119   public SerializedPart copy() {
120      return new SerializedPart(this);
121   }
122
123   /**
124    * Sets the HTTP part type.
125    *
126    * @param value The new value for this property.
127    * @return This object.
128    */
129   public SerializedPart type(HttpPartType value) {
130      type = value;
131      return this;
132   }
133
134   /**
135    * Sets the serializer to use for serializing the value to a string value.
136    *
137    * @param value The new value for this property.
138    * @return This object.
139    */
140   public SerializedPart serializer(HttpPartSerializer value) {
141      if (value != null)
142         return serializer(value.getPartSession());
143      return this;
144   }
145
146   /**
147    * Sets the serializer to use for serializing the value to a string value.
148    *
149    * @param value The new value for this property.
150    * @return This object.
151    */
152   public SerializedPart serializer(HttpPartSerializerSession value) {
153      serializer = value;
154      return this;
155   }
156
157   /**
158    * Sets the schema object that defines the format of the output.
159    *
160    * @param value The new value for this property.
161    * @return This object.
162    */
163   public SerializedPart schema(HttpPartSchema value) {
164      this.schema = value;
165      return this;
166   }
167
168   /**
169    * Copies this bean and sets the serializer and schema on it.
170    *
171    * @param serializer The new serializer for the bean.  Can be <jk>null</jk>.
172    * @param schema The new schema for the bean.  Can be <jk>null</jk>.
173    * @return Either a new bean with the serializer set, or this bean if
174    *    both values are <jk>null</jk> or the serializer and schema were already set.
175    */
176   public SerializedPart copyWith(HttpPartSerializerSession serializer, HttpPartSchema schema) {
177      if ((this.serializer == null && serializer != null) || (this.schema == null && schema != null)) {
178         SerializedPart p = copy();
179         if (serializer != null)
180            p.serializer(serializer);
181         if (schema != null)
182            p.schema(schema);
183         return p;
184      }
185      return this;
186   }
187
188   /**
189    * Don't serialize this pair if the value is <jk>null</jk> or an empty string.
190    *
191    * @return This object.
192    */
193   public SerializedPart skipIfEmpty() {
194      return skipIfEmpty(true);
195   }
196
197   /**
198    * Don't serialize this pair if the value is <jk>null</jk> or an empty string.
199    *
200    * @param value The new value of this setting.
201    * @return This object.
202    */
203   public SerializedPart skipIfEmpty(boolean value) {
204      this.skipIfEmpty = value;
205      return this;
206   }
207
208   @Override /* Headerable */
209   public SerializedHeader asHeader() {
210      return new SerializedHeader(getName(), value, serializer, schema, skipIfEmpty);
211   }
212
213   @Override /* NameValuePair */
214   public String getValue() {
215      try {
216         Object v = unwrap(value);
217         HttpPartSchema schema = this.schema == null ? HttpPartSchema.DEFAULT : this.schema;
218         String def = schema.getDefault();
219         if (v == null) {
220            if ((def == null && ! schema.isRequired()) || (def == null && schema.isAllowEmptyValue()))
221               return null;
222         }
223         if (isEmpty(stringify(v)) && skipIfEmpty && def == null)
224            return null;
225         return serializer == null ? stringify(v) : serializer.serialize(type, schema, v);
226      } catch (SchemaValidationException e) {
227         throw new BasicRuntimeException(e, "Validation error on request {0} part ''{1}''=''{2}''", type, getName(), value);
228      } catch (SerializeException e) {
229         throw new BasicRuntimeException(e, "Serialization error on request {0} part ''{1}''", type, getName());
230      }
231   }
232
233   private Object unwrap(Object o) {
234      if (o instanceof Supplier)
235         return ((Supplier<?>)o).get();
236      return o;
237   }
238}