1 package com.fasterxml.jackson.databind.ser.impl; 2 3 import java.util.Arrays; 4 5 import com.fasterxml.jackson.databind.BeanProperty; 6 import com.fasterxml.jackson.databind.JavaType; 7 import com.fasterxml.jackson.databind.JsonMappingException; 8 import com.fasterxml.jackson.databind.JsonSerializer; 9 import com.fasterxml.jackson.databind.SerializerProvider; 10 11 /** 12 * Helper container used for resolving serializers for dynamic (possibly but not 13 * necessarily polymorphic) properties: properties whose type is not forced 14 * to use dynamic (declared) type and that are not final. 15 * If so, serializer to use can only be established once actual value type is known. 16 * Since this happens a lot unless static typing is forced (or types are final) 17 * this implementation is optimized for efficiency. 18 * Instances are immutable; new instances are created with factory methods: this 19 * is important to ensure correct multi-threaded access. 20 */ 21 public abstract class PropertySerializerMap 22 { 23 /** 24 * Configuration setting that determines what happens when maximum 25 * size (currently 8) is reached: if true, will "start from beginning"; 26 * if false, will simply stop adding new entries. 27 * 28 * @since 2.5 29 */ 30 protected final boolean _resetWhenFull; 31 32 /** 33 * @since 2.5 34 */ PropertySerializerMap(boolean resetWhenFull)35 protected PropertySerializerMap(boolean resetWhenFull) { 36 _resetWhenFull = resetWhenFull; 37 } 38 PropertySerializerMap(PropertySerializerMap base)39 protected PropertySerializerMap(PropertySerializerMap base) { 40 _resetWhenFull = base._resetWhenFull; 41 } 42 43 /** 44 * Main lookup method. Takes a "raw" type since usage is always from 45 * place where parameterization is fixed such that there cannot be 46 * type-parametric variations. 47 */ serializerFor(Class<?> type)48 public abstract JsonSerializer<Object> serializerFor(Class<?> type); 49 50 /** 51 * Method called if initial lookup fails, when looking for a primary 52 * serializer (one that is directly attached to a property). 53 * Will both find serializer 54 * and construct new map instance if warranted, and return both. 55 * 56 * @since 2.3 57 * 58 * @throws JsonMappingException 59 */ findAndAddPrimarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)60 public final SerializerAndMapResult findAndAddPrimarySerializer(Class<?> type, 61 SerializerProvider provider, BeanProperty property) 62 throws JsonMappingException 63 { 64 JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property); 65 return new SerializerAndMapResult(serializer, newWith(type, serializer)); 66 } 67 findAndAddPrimarySerializer(JavaType type, SerializerProvider provider, BeanProperty property)68 public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type, 69 SerializerProvider provider, BeanProperty property) 70 throws JsonMappingException 71 { 72 JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property); 73 return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); 74 } 75 76 /** 77 * Method called if initial lookup fails, when looking for a non-primary 78 * serializer (one that is not directly attached to a property). 79 * Will both find serializer 80 * and construct new map instance if warranted, and return both. 81 * 82 * @since 2.3 83 * 84 * @throws JsonMappingException 85 */ findAndAddSecondarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)86 public final SerializerAndMapResult findAndAddSecondarySerializer(Class<?> type, 87 SerializerProvider provider, BeanProperty property) 88 throws JsonMappingException 89 { 90 JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property); 91 return new SerializerAndMapResult(serializer, newWith(type, serializer)); 92 } 93 findAndAddSecondarySerializer(JavaType type, SerializerProvider provider, BeanProperty property)94 public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type, 95 SerializerProvider provider, BeanProperty property) 96 throws JsonMappingException 97 { 98 JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property); 99 return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); 100 } 101 102 /** 103 * Method called if initial lookup fails, when looking for a root value 104 * serializer: one that is not directly attached to a property, but needs to 105 * have {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} wrapped 106 * around it. Will both find the serializer 107 * and construct new map instance if warranted, and return both. 108 * 109 * @since 2.5 110 * 111 * @throws JsonMappingException 112 */ findAndAddRootValueSerializer(Class<?> type, SerializerProvider provider)113 public final SerializerAndMapResult findAndAddRootValueSerializer(Class<?> type, 114 SerializerProvider provider) 115 throws JsonMappingException 116 { 117 JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null); 118 return new SerializerAndMapResult(serializer, newWith(type, serializer)); 119 } 120 121 /** 122 * @since 2.5 123 */ findAndAddRootValueSerializer(JavaType type, SerializerProvider provider)124 public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type, 125 SerializerProvider provider) 126 throws JsonMappingException 127 { 128 JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null); 129 return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); 130 } 131 132 /** 133 * Method called if initial lookup fails, when looking for a key 134 * serializer (possible attached indirectly to a property) 135 * Will both find serializer 136 * and construct new map instance if warranted, and return both. 137 * 138 * @since 2.7 139 */ findAndAddKeySerializer(Class<?> type, SerializerProvider provider, BeanProperty property)140 public final SerializerAndMapResult findAndAddKeySerializer(Class<?> type, 141 SerializerProvider provider, BeanProperty property) 142 throws JsonMappingException 143 { 144 JsonSerializer<Object> serializer = provider.findKeySerializer(type, property); 145 return new SerializerAndMapResult(serializer, newWith(type, serializer)); 146 } 147 148 /** 149 * Method that can be used to 'register' a serializer that caller has resolved 150 * without help of this map. 151 * 152 * @since 2.5 153 */ addSerializer(Class<?> type, JsonSerializer<Object> serializer)154 public final SerializerAndMapResult addSerializer(Class<?> type, JsonSerializer<Object> serializer) { 155 return new SerializerAndMapResult(serializer, newWith(type, serializer)); 156 } 157 158 /** 159 * @since 2.5 160 */ addSerializer(JavaType type, JsonSerializer<Object> serializer)161 public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer<Object> serializer) { 162 return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); 163 } 164 newWith(Class<?> type, JsonSerializer<Object> serializer)165 public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer); 166 167 /** 168 * @since 2.5 169 */ emptyForProperties()170 public static PropertySerializerMap emptyForProperties() { 171 return Empty.FOR_PROPERTIES; 172 } 173 174 /** 175 * @since 2.5 176 */ emptyForRootValues()177 public static PropertySerializerMap emptyForRootValues() { 178 return Empty.FOR_ROOT_VALUES; 179 } 180 181 /* 182 /********************************************************** 183 /* Helper classes 184 /********************************************************** 185 */ 186 187 /** 188 * Value class used for returning tuple that has both serializer 189 * that was retrieved and new map instance 190 */ 191 public final static class SerializerAndMapResult 192 { 193 public final JsonSerializer<Object> serializer; 194 public final PropertySerializerMap map; 195 SerializerAndMapResult(JsonSerializer<Object> serializer, PropertySerializerMap map)196 public SerializerAndMapResult(JsonSerializer<Object> serializer, 197 PropertySerializerMap map) 198 { 199 this.serializer = serializer; 200 this.map = map; 201 } 202 } 203 204 /** 205 * Trivial container for bundling type + serializer entries. 206 */ 207 private final static class TypeAndSerializer 208 { 209 public final Class<?> type; 210 public final JsonSerializer<Object> serializer; 211 TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer)212 public TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer) { 213 this.type = type; 214 this.serializer = serializer; 215 } 216 } 217 218 /* 219 /********************************************************** 220 /* Implementations 221 /********************************************************** 222 */ 223 224 /** 225 * Bogus instance that contains no serializers; used as the default 226 * map with new serializers. 227 */ 228 private final static class Empty extends PropertySerializerMap 229 { 230 // No root serializers; do not reset when full 231 public final static Empty FOR_PROPERTIES = new Empty(false); 232 233 // Yes, root serializers; do reset when full 234 public final static Empty FOR_ROOT_VALUES = new Empty(true); 235 Empty(boolean resetWhenFull)236 protected Empty(boolean resetWhenFull) { 237 super(resetWhenFull); 238 } 239 240 @Override serializerFor(Class<?> type)241 public JsonSerializer<Object> serializerFor(Class<?> type) { 242 return null; // empty, nothing to find 243 } 244 245 @Override newWith(Class<?> type, JsonSerializer<Object> serializer)246 public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { 247 return new Single(this, type, serializer); 248 } 249 } 250 251 /** 252 * Map that contains a single serializer; although seemingly silly 253 * this is probably the most commonly used variant because many 254 * theoretically dynamic or polymorphic types just have single 255 * actual type. 256 */ 257 private final static class Single extends PropertySerializerMap 258 { 259 private final Class<?> _type; 260 private final JsonSerializer<Object> _serializer; 261 Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer)262 public Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer) { 263 super(base); 264 _type = type; 265 _serializer = serializer; 266 } 267 268 @Override serializerFor(Class<?> type)269 public JsonSerializer<Object> serializerFor(Class<?> type) 270 { 271 if (type == _type) { 272 return _serializer; 273 } 274 return null; 275 } 276 277 @Override newWith(Class<?> type, JsonSerializer<Object> serializer)278 public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { 279 return new Double(this, _type, _serializer, type, serializer); 280 } 281 } 282 283 private final static class Double extends PropertySerializerMap 284 { 285 private final Class<?> _type1, _type2; 286 private final JsonSerializer<Object> _serializer1, _serializer2; 287 Double(PropertySerializerMap base, Class<?> type1, JsonSerializer<Object> serializer1, Class<?> type2, JsonSerializer<Object> serializer2)288 public Double(PropertySerializerMap base, 289 Class<?> type1, JsonSerializer<Object> serializer1, 290 Class<?> type2, JsonSerializer<Object> serializer2) 291 { 292 super(base); 293 _type1 = type1; 294 _serializer1 = serializer1; 295 _type2 = type2; 296 _serializer2 = serializer2; 297 } 298 299 @Override serializerFor(Class<?> type)300 public JsonSerializer<Object> serializerFor(Class<?> type) 301 { 302 if (type == _type1) { 303 return _serializer1; 304 } 305 if (type == _type2) { 306 return _serializer2; 307 } 308 return null; 309 } 310 311 @Override newWith(Class<?> type, JsonSerializer<Object> serializer)312 public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { 313 // Ok: let's just create generic one 314 TypeAndSerializer[] ts = new TypeAndSerializer[3]; 315 ts[0] = new TypeAndSerializer(_type1, _serializer1); 316 ts[1] = new TypeAndSerializer(_type2, _serializer2); 317 ts[2] = new TypeAndSerializer(type, serializer); 318 return new Multi(this, ts); 319 } 320 } 321 322 private final static class Multi extends PropertySerializerMap 323 { 324 /** 325 * Let's limit number of serializers we actually cache; linear 326 * lookup won't scale too well beyond smallish number, and if 327 * we really want to support larger collections should use 328 * a hash map. But it seems unlikely this is a common use 329 * case so for now let's just stop building after hard-coded 330 * limit. 8 sounds like a reasonable stab for now. 331 */ 332 private final static int MAX_ENTRIES = 8; 333 334 private final TypeAndSerializer[] _entries; 335 Multi(PropertySerializerMap base, TypeAndSerializer[] entries)336 public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) { 337 super(base); 338 _entries = entries; 339 } 340 341 @Override serializerFor(Class<?> type)342 public JsonSerializer<Object> serializerFor(Class<?> type) 343 { 344 // Always have first 3 populated so 345 TypeAndSerializer entry; 346 entry = _entries[0]; 347 if (entry.type == type) return entry.serializer; 348 entry = _entries[1]; 349 if (entry.type == type) return entry.serializer; 350 entry = _entries[2]; 351 if (entry.type == type) return entry.serializer; 352 353 switch (_entries.length) { 354 case 8: 355 entry = _entries[7]; 356 if (entry.type == type) return entry.serializer; 357 case 7: 358 entry = _entries[6]; 359 if (entry.type == type) return entry.serializer; 360 case 6: 361 entry = _entries[5]; 362 if (entry.type == type) return entry.serializer; 363 case 5: 364 entry = _entries[4]; 365 if (entry.type == type) return entry.serializer; 366 case 4: 367 entry = _entries[3]; 368 if (entry.type == type) return entry.serializer; 369 default: 370 } 371 return null; 372 } 373 374 @Override newWith(Class<?> type, JsonSerializer<Object> serializer)375 public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) 376 { 377 int len = _entries.length; 378 // Will only grow up to N entries. We could consider couple of alternatives after 379 // this if we wanted to... but for now, two main choices make most sense 380 if (len == MAX_ENTRIES) { 381 if (_resetWhenFull) { 382 return new Single(this, type, serializer); 383 } 384 return this; 385 } 386 TypeAndSerializer[] entries = Arrays.copyOf(_entries, len+1); 387 entries[len] = new TypeAndSerializer(type, serializer); 388 return new Multi(this, entries); 389 } 390 } 391 } 392