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