package com.fasterxml.jackson.databind.ser.impl;

import java.util.Arrays;

import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

Helper container used for resolving serializers for dynamic (possibly but not necessarily polymorphic) properties: properties whose type is not forced to use dynamic (declared) type and that are not final. If so, serializer to use can only be established once actual value type is known. Since this happens a lot unless static typing is forced (or types are final) this implementation is optimized for efficiency. Instances are immutable; new instances are created with factory methods: this is important to ensure correct multi-threaded access.
/** * Helper container used for resolving serializers for dynamic (possibly but not * necessarily polymorphic) properties: properties whose type is not forced * to use dynamic (declared) type and that are not final. * If so, serializer to use can only be established once actual value type is known. * Since this happens a lot unless static typing is forced (or types are final) * this implementation is optimized for efficiency. * Instances are immutable; new instances are created with factory methods: this * is important to ensure correct multi-threaded access. */
public abstract class PropertySerializerMap {
Configuration setting that determines what happens when maximum size (currently 8) is reached: if true, will "start from beginning"; if false, will simply stop adding new entries.
Since:2.5
/** * Configuration setting that determines what happens when maximum * size (currently 8) is reached: if true, will "start from beginning"; * if false, will simply stop adding new entries. * * @since 2.5 */
protected final boolean _resetWhenFull;
Since:2.5
/** * @since 2.5 */
protected PropertySerializerMap(boolean resetWhenFull) { _resetWhenFull = resetWhenFull; } protected PropertySerializerMap(PropertySerializerMap base) { _resetWhenFull = base._resetWhenFull; }
Main lookup method. Takes a "raw" type since usage is always from place where parameterization is fixed such that there cannot be type-parametric variations.
/** * Main lookup method. Takes a "raw" type since usage is always from * place where parameterization is fixed such that there cannot be * type-parametric variations. */
public abstract JsonSerializer<Object> serializerFor(Class<?> type);
Method called if initial lookup fails, when looking for a primary serializer (one that is directly attached to a property). Will both find serializer and construct new map instance if warranted, and return both.
Throws:
Since:2.3
/** * Method called if initial lookup fails, when looking for a primary * serializer (one that is directly attached to a property). * Will both find serializer * and construct new map instance if warranted, and return both. * * @since 2.3 * * @throws JsonMappingException */
public final SerializerAndMapResult findAndAddPrimarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type, serializer)); } public final SerializerAndMapResult findAndAddPrimarySerializer(JavaType type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findPrimaryPropertySerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); }
Method called if initial lookup fails, when looking for a non-primary serializer (one that is not directly attached to a property). Will both find serializer and construct new map instance if warranted, and return both.
Throws:
Since:2.3
/** * Method called if initial lookup fails, when looking for a non-primary * serializer (one that is not directly attached to a property). * Will both find serializer * and construct new map instance if warranted, and return both. * * @since 2.3 * * @throws JsonMappingException */
public final SerializerAndMapResult findAndAddSecondarySerializer(Class<?> type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type, serializer)); } public final SerializerAndMapResult findAndAddSecondarySerializer(JavaType type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findContentValueSerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); }
Method called if initial lookup fails, when looking for a root value serializer: one that is not directly attached to a property, but needs to have TypeSerializer wrapped around it. Will both find the serializer and construct new map instance if warranted, and return both.
Throws:
Since:2.5
/** * Method called if initial lookup fails, when looking for a root value * serializer: one that is not directly attached to a property, but needs to * have {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer} wrapped * around it. Will both find the serializer * and construct new map instance if warranted, and return both. * * @since 2.5 * * @throws JsonMappingException */
public final SerializerAndMapResult findAndAddRootValueSerializer(Class<?> type, SerializerProvider provider) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null); return new SerializerAndMapResult(serializer, newWith(type, serializer)); }
Since:2.5
/** * @since 2.5 */
public final SerializerAndMapResult findAndAddRootValueSerializer(JavaType type, SerializerProvider provider) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findTypedValueSerializer(type, false, null); return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); }
Method called if initial lookup fails, when looking for a key serializer (possible attached indirectly to a property) Will both find serializer and construct new map instance if warranted, and return both.
Since:2.7
/** * Method called if initial lookup fails, when looking for a key * serializer (possible attached indirectly to a property) * Will both find serializer * and construct new map instance if warranted, and return both. * * @since 2.7 */
public final SerializerAndMapResult findAndAddKeySerializer(Class<?> type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findKeySerializer(type, property); return new SerializerAndMapResult(serializer, newWith(type, serializer)); }
Method that can be used to 'register' a serializer that caller has resolved without help of this map.
Since:2.5
/** * Method that can be used to 'register' a serializer that caller has resolved * without help of this map. * * @since 2.5 */
public final SerializerAndMapResult addSerializer(Class<?> type, JsonSerializer<Object> serializer) { return new SerializerAndMapResult(serializer, newWith(type, serializer)); }
Since:2.5
/** * @since 2.5 */
public final SerializerAndMapResult addSerializer(JavaType type, JsonSerializer<Object> serializer) { return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer)); } public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer);
Since:2.5
/** * @since 2.5 */
public static PropertySerializerMap emptyForProperties() { return Empty.FOR_PROPERTIES; }
Since:2.5
/** * @since 2.5 */
public static PropertySerializerMap emptyForRootValues() { return Empty.FOR_ROOT_VALUES; } /* /********************************************************** /* Helper classes /********************************************************** */
Value class used for returning tuple that has both serializer that was retrieved and new map instance
/** * Value class used for returning tuple that has both serializer * that was retrieved and new map instance */
public final static class SerializerAndMapResult { public final JsonSerializer<Object> serializer; public final PropertySerializerMap map; public SerializerAndMapResult(JsonSerializer<Object> serializer, PropertySerializerMap map) { this.serializer = serializer; this.map = map; } }
Trivial container for bundling type + serializer entries.
/** * Trivial container for bundling type + serializer entries. */
private final static class TypeAndSerializer { public final Class<?> type; public final JsonSerializer<Object> serializer; public TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer) { this.type = type; this.serializer = serializer; } } /* /********************************************************** /* Implementations /********************************************************** */
Bogus instance that contains no serializers; used as the default map with new serializers.
/** * Bogus instance that contains no serializers; used as the default * map with new serializers. */
private final static class Empty extends PropertySerializerMap { // No root serializers; do not reset when full public final static Empty FOR_PROPERTIES = new Empty(false); // Yes, root serializers; do reset when full public final static Empty FOR_ROOT_VALUES = new Empty(true); protected Empty(boolean resetWhenFull) { super(resetWhenFull); } @Override public JsonSerializer<Object> serializerFor(Class<?> type) { return null; // empty, nothing to find } @Override public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { return new Single(this, type, serializer); } }
Map that contains a single serializer; although seemingly silly this is probably the most commonly used variant because many theoretically dynamic or polymorphic types just have single actual type.
/** * Map that contains a single serializer; although seemingly silly * this is probably the most commonly used variant because many * theoretically dynamic or polymorphic types just have single * actual type. */
private final static class Single extends PropertySerializerMap { private final Class<?> _type; private final JsonSerializer<Object> _serializer; public Single(PropertySerializerMap base, Class<?> type, JsonSerializer<Object> serializer) { super(base); _type = type; _serializer = serializer; } @Override public JsonSerializer<Object> serializerFor(Class<?> type) { if (type == _type) { return _serializer; } return null; } @Override public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { return new Double(this, _type, _serializer, type, serializer); } } private final static class Double extends PropertySerializerMap { private final Class<?> _type1, _type2; private final JsonSerializer<Object> _serializer1, _serializer2; public Double(PropertySerializerMap base, Class<?> type1, JsonSerializer<Object> serializer1, Class<?> type2, JsonSerializer<Object> serializer2) { super(base); _type1 = type1; _serializer1 = serializer1; _type2 = type2; _serializer2 = serializer2; } @Override public JsonSerializer<Object> serializerFor(Class<?> type) { if (type == _type1) { return _serializer1; } if (type == _type2) { return _serializer2; } return null; } @Override public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { // Ok: let's just create generic one TypeAndSerializer[] ts = new TypeAndSerializer[3]; ts[0] = new TypeAndSerializer(_type1, _serializer1); ts[1] = new TypeAndSerializer(_type2, _serializer2); ts[2] = new TypeAndSerializer(type, serializer); return new Multi(this, ts); } } private final static class Multi extends PropertySerializerMap {
Let's limit number of serializers we actually cache; linear lookup won't scale too well beyond smallish number, and if we really want to support larger collections should use a hash map. But it seems unlikely this is a common use case so for now let's just stop building after hard-coded limit. 8 sounds like a reasonable stab for now.
/** * Let's limit number of serializers we actually cache; linear * lookup won't scale too well beyond smallish number, and if * we really want to support larger collections should use * a hash map. But it seems unlikely this is a common use * case so for now let's just stop building after hard-coded * limit. 8 sounds like a reasonable stab for now. */
private final static int MAX_ENTRIES = 8; private final TypeAndSerializer[] _entries; public Multi(PropertySerializerMap base, TypeAndSerializer[] entries) { super(base); _entries = entries; } @Override public JsonSerializer<Object> serializerFor(Class<?> type) { // Always have first 3 populated so TypeAndSerializer entry; entry = _entries[0]; if (entry.type == type) return entry.serializer; entry = _entries[1]; if (entry.type == type) return entry.serializer; entry = _entries[2]; if (entry.type == type) return entry.serializer; switch (_entries.length) { case 8: entry = _entries[7]; if (entry.type == type) return entry.serializer; case 7: entry = _entries[6]; if (entry.type == type) return entry.serializer; case 6: entry = _entries[5]; if (entry.type == type) return entry.serializer; case 5: entry = _entries[4]; if (entry.type == type) return entry.serializer; case 4: entry = _entries[3]; if (entry.type == type) return entry.serializer; default: } return null; } @Override public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) { int len = _entries.length; // Will only grow up to N entries. We could consider couple of alternatives after // this if we wanted to... but for now, two main choices make most sense if (len == MAX_ENTRIES) { if (_resetWhenFull) { return new Single(this, type, serializer); } return this; } TypeAndSerializer[] entries = Arrays.copyOf(_entries, len+1); entries[len] = new TypeAndSerializer(type, serializer); return new Multi(this, entries); } } }