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.msgpack; 014 015import static org.apache.juneau.common.internal.IOUtils.*; 016import static org.apache.juneau.internal.CollectionUtils.*; 017 018import java.io.*; 019import java.lang.reflect.*; 020import java.util.*; 021import java.util.function.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.httppart.*; 025import org.apache.juneau.internal.*; 026import org.apache.juneau.serializer.*; 027import org.apache.juneau.svl.*; 028import org.apache.juneau.swap.*; 029 030/** 031 * Session object that lives for the duration of a single use of {@link MsgPackSerializer}. 032 * 033 * <h5 class='section'>Notes:</h5><ul> 034 * <li class='warn'>This class is not thread safe and is typically discarded after one use. 035 * </ul> 036 * 037 * <h5 class='section'>See Also:</h5><ul> 038 * <li class='link'><a class="doclink" href="../../../../index.html#jm.MsgPackDetails">MessagePack Details</a> 039 040 * </ul> 041 */ 042public final class MsgPackSerializerSession extends OutputStreamSerializerSession { 043 044 //----------------------------------------------------------------------------------------------------------------- 045 // Static 046 //----------------------------------------------------------------------------------------------------------------- 047 048 /** 049 * Creates a new builder for this object. 050 * 051 * @param ctx The context creating this session. 052 * @return A new builder. 053 */ 054 public static Builder create(MsgPackSerializer ctx) { 055 return new Builder(ctx); 056 } 057 058 //----------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //----------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 */ 065 @FluentSetters 066 public static class Builder extends OutputStreamSerializerSession.Builder { 067 068 MsgPackSerializer ctx; 069 070 /** 071 * Constructor 072 * 073 * @param ctx The context creating this session. 074 */ 075 protected Builder(MsgPackSerializer ctx) { 076 super(ctx); 077 this.ctx = ctx; 078 } 079 080 @Override 081 public MsgPackSerializerSession build() { 082 return new MsgPackSerializerSession(this); 083 } 084 085 // <FluentSetters> 086 087 @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */ 088 public <T> Builder apply(Class<T> type, Consumer<T> apply) { 089 super.apply(type, apply); 090 return this; 091 } 092 093 @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */ 094 public Builder debug(Boolean value) { 095 super.debug(value); 096 return this; 097 } 098 099 @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */ 100 public Builder properties(Map<String,Object> value) { 101 super.properties(value); 102 return this; 103 } 104 105 @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */ 106 public Builder property(String key, Object value) { 107 super.property(key, value); 108 return this; 109 } 110 111 @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */ 112 public Builder unmodifiable() { 113 super.unmodifiable(); 114 return this; 115 } 116 117 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 118 public Builder locale(Locale value) { 119 super.locale(value); 120 return this; 121 } 122 123 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 124 public Builder localeDefault(Locale value) { 125 super.localeDefault(value); 126 return this; 127 } 128 129 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 130 public Builder mediaType(MediaType value) { 131 super.mediaType(value); 132 return this; 133 } 134 135 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 136 public Builder mediaTypeDefault(MediaType value) { 137 super.mediaTypeDefault(value); 138 return this; 139 } 140 141 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 142 public Builder timeZone(TimeZone value) { 143 super.timeZone(value); 144 return this; 145 } 146 147 @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */ 148 public Builder timeZoneDefault(TimeZone value) { 149 super.timeZoneDefault(value); 150 return this; 151 } 152 153 @Override /* GENERATED - org.apache.juneau.serializer.SerializerSession.Builder */ 154 public Builder javaMethod(Method value) { 155 super.javaMethod(value); 156 return this; 157 } 158 159 @Override /* GENERATED - org.apache.juneau.serializer.SerializerSession.Builder */ 160 public Builder resolver(VarResolverSession value) { 161 super.resolver(value); 162 return this; 163 } 164 165 @Override /* GENERATED - org.apache.juneau.serializer.SerializerSession.Builder */ 166 public Builder schema(HttpPartSchema value) { 167 super.schema(value); 168 return this; 169 } 170 171 @Override /* GENERATED - org.apache.juneau.serializer.SerializerSession.Builder */ 172 public Builder schemaDefault(HttpPartSchema value) { 173 super.schemaDefault(value); 174 return this; 175 } 176 177 @Override /* GENERATED - org.apache.juneau.serializer.SerializerSession.Builder */ 178 public Builder uriContext(UriContext value) { 179 super.uriContext(value); 180 return this; 181 } 182 183 // </FluentSetters> 184 } 185 186 //----------------------------------------------------------------------------------------------------------------- 187 // Instance 188 //----------------------------------------------------------------------------------------------------------------- 189 190 private final MsgPackSerializer ctx; 191 192 /** 193 * Constructor. 194 * 195 * @param builder The builder for this object. 196 */ 197 protected MsgPackSerializerSession(Builder builder) { 198 super(builder); 199 ctx = builder.ctx; 200 } 201 202 @Override /* SerializerSession */ 203 protected void doSerialize(SerializerPipe out, Object o) throws IOException, SerializeException { 204 serializeAnything(getMsgPackOutputStream(out), o, getExpectedRootType(o), "root", null); 205 } 206 207 /* 208 * Converts the specified output target object to an {@link MsgPackOutputStream}. 209 */ 210 private static MsgPackOutputStream getMsgPackOutputStream(SerializerPipe out) throws IOException { 211 Object output = out.getRawOutput(); 212 if (output instanceof MsgPackOutputStream) 213 return (MsgPackOutputStream)output; 214 MsgPackOutputStream os = new MsgPackOutputStream(out.getOutputStream()); 215 out.setOutputStream(os); 216 return os; 217 } 218 219 /* 220 * Workhorse method. 221 * Determines the type of object, and then calls the appropriate type-specific serialization method. 222 */ 223 @SuppressWarnings({ "rawtypes" }) 224 private MsgPackOutputStream serializeAnything(MsgPackOutputStream out, Object o, ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws SerializeException { 225 226 if (o == null) 227 return out.appendNull(); 228 229 if (eType == null) 230 eType = object(); 231 232 ClassMeta<?> aType; // The actual type 233 ClassMeta<?> sType; // The serialized type 234 235 aType = push2(attrName, o, eType); 236 boolean isRecursion = aType == null; 237 238 // Handle recursion 239 if (aType == null) 240 return out.appendNull(); 241 242 // Handle Optional<X> 243 if (isOptional(aType)) { 244 o = getOptionalValue(o); 245 eType = getOptionalType(eType); 246 aType = getClassMetaForObject(o, object()); 247 } 248 249 sType = aType; 250 String typeName = getBeanTypeName(this, eType, aType, pMeta); 251 252 // Swap if necessary 253 ObjectSwap swap = aType.getSwap(this); 254 if (swap != null) { 255 o = swap(swap, o); 256 sType = swap.getSwapClassMeta(this); 257 258 // If the getSwapClass() method returns Object, we need to figure out 259 // the actual type now. 260 if (sType.isObject()) 261 sType = getClassMetaForObject(o); 262 } 263 264 // '\0' characters are considered null. 265 if (o == null || (sType.isChar() && ((Character)o).charValue() == 0)) 266 out.appendNull(); 267 else if (sType.isBoolean()) 268 out.appendBoolean((Boolean)o); 269 else if (sType.isNumber()) 270 out.appendNumber((Number)o); 271 else if (sType.isBean()) 272 serializeBeanMap(out, toBeanMap(o), typeName); 273 else if (sType.isUri() || (pMeta != null && pMeta.isUri())) 274 out.appendString(resolveUri(o.toString())); 275 else if (sType.isMap()) { 276 if (o instanceof BeanMap) 277 serializeBeanMap(out, (BeanMap)o, typeName); 278 else 279 serializeMap(out, (Map)o, eType); 280 } 281 else if (sType.isCollection()) { 282 serializeCollection(out, (Collection) o, eType); 283 } 284 else if (sType.isByteArray()) { 285 out.appendBinary((byte[])o); 286 } 287 else if (sType.isArray()) { 288 serializeCollection(out, toList(sType.getInnerClass(), o), eType); 289 } 290 else if (sType.isReader()) { 291 pipe((Reader)o, out, SerializerSession::handleThrown); 292 } 293 else if (sType.isInputStream()) { 294 pipe((InputStream)o, out, SerializerSession::handleThrown); 295 } 296 else 297 out.appendString(toString(o)); 298 299 if (! isRecursion) 300 pop(); 301 return out; 302 } 303 304 @SuppressWarnings({ "rawtypes", "unchecked" }) 305 private void serializeMap(MsgPackOutputStream out, Map m, ClassMeta<?> type) throws SerializeException { 306 307 ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); 308 309 m = sort(m); 310 311 // The map size may change as we're iterating over it, so 312 // grab a snapshot of the entries in a separate list. 313 List<SimpleMapEntry> entries = list(m.size()); 314 m.forEach((k,v) -> entries.add(new SimpleMapEntry(k, v))); 315 316 out.startMap(entries.size()); 317 318 entries.forEach(x -> { 319 Object value = x.value; 320 Object key = generalize(x.key, keyType); 321 serializeAnything(out, key, keyType, null, null); 322 serializeAnything(out, value, valueType, null, null); 323 }); 324 } 325 326 private void serializeBeanMap(MsgPackOutputStream out, final BeanMap<?> m, String typeName) throws SerializeException { 327 328 Predicate<Object> checkNull = x -> isKeepNullProperties() || x != null; 329 330 List<BeanPropertyValue> values = new ArrayList<>(); 331 332 if (typeName != null) { 333 BeanPropertyMeta pm = m.getMeta().getTypeProperty(); 334 values.add(new BeanPropertyValue(pm, pm.getName(), typeName, null)); 335 } 336 337 m.forEachValue(checkNull, (pMeta,key,value,thrown) -> { 338 if (thrown != null) { 339 onBeanGetterException(pMeta, thrown); 340 return; 341 } 342 BeanPropertyValue p = new BeanPropertyValue(pMeta, key, value, null); 343 344 if ((! isKeepNullProperties()) && willRecurse(p)) { 345 return; // Must handle the case where recursion occurs and property is not serialized. 346 } 347 348 values.add(p); 349 }); 350 351 out.startMap(values.size()); 352 353 values.forEach(x -> { 354 BeanPropertyMeta pMeta = x.getMeta(); 355 if (pMeta.canRead()) { 356 ClassMeta<?> cMeta = x.getClassMeta(); 357 String key = x.getName(); 358 Object value = x.getValue(); 359 serializeAnything(out, key, null, null, null); 360 serializeAnything(out, value, cMeta, key, pMeta); 361 } 362 }); 363 } 364 365 private boolean willRecurse(BeanPropertyValue v) throws SerializeException { 366 ClassMeta<?> aType = push2(v.getName(), v.getValue(), v.getClassMeta()); 367 if (aType != null) 368 pop(); 369 return aType == null; 370 } 371 372 private static final class SimpleMapEntry { 373 final Object key; 374 final Object value; 375 376 SimpleMapEntry(Object key, Object value) { 377 this.key = key; 378 this.value = value; 379 } 380 } 381 382 @SuppressWarnings({"rawtypes", "unchecked"}) 383 private void serializeCollection(MsgPackOutputStream out, Collection c, ClassMeta<?> type) throws SerializeException { 384 ClassMeta<?> elementType = type.getElementType(); 385 List<Object> l = list(c.size()); 386 c = sort(c); 387 l.addAll(c); 388 out.startArray(l.size()); 389 l.forEach(x -> serializeAnything(out, x, elementType, "<iterator>", null)); 390 } 391 392 //----------------------------------------------------------------------------------------------------------------- 393 // Properties 394 //----------------------------------------------------------------------------------------------------------------- 395 396 @Override 397 protected boolean isAddBeanTypes() { 398 return ctx.isAddBeanTypes(); 399 } 400}