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}