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