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);
Deprecated: Since 2.5 Use emptyForProperties
instead
/**
* @deprecated Since 2.5 Use {@link #emptyForProperties} instead
*/
@Deprecated
public static PropertySerializerMap emptyMap() {
return emptyForProperties();
}
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);
}
}
}