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}