blob: 821de5d1fd8187da8232b946f5c02a267aabb984 [file] [log] [blame]
package org.codehaus.jackson.map.ser.impl;
import org.codehaus.jackson.map.BeanProperty;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.type.JavaType;
/**
* 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.
*
* @since 1.7
*/
public abstract class PropertySerializerMap
{
/**
* Main lookup method. Takes a "raw" type since usage is always from
* place where parameterization is fixed such that there can not be
* type-parametric variations.
*/
public abstract JsonSerializer<Object> serializerFor(Class<?> type);
/**
* Method called if initial lookup fails; will both find serializer
* and construct new map instance if warranted, and return both
* @throws JsonMappingException
*/
public final SerializerAndMapResult findAndAddSerializer(Class<?> type,
SerializerProvider provider, BeanProperty property)
throws JsonMappingException
{
JsonSerializer<Object> serializer = provider.findValueSerializer(type, property);
return new SerializerAndMapResult(serializer, newWith(type, serializer));
}
public final SerializerAndMapResult findAndAddSerializer(JavaType type,
SerializerProvider provider, BeanProperty property)
throws JsonMappingException
{
JsonSerializer<Object> serializer = provider.findValueSerializer(type, property);
return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
}
public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer);
public static PropertySerializerMap emptyMap() {
return Empty.instance;
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* 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.
*/
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.
*/
private final static class Empty extends PropertySerializerMap
{
protected final static Empty instance = new Empty();
@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(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.
*/
private final static class Single extends PropertySerializerMap
{
private final Class<?> _type;
private final JsonSerializer<Object> _serializer;
public Single(Class<?> type, JsonSerializer<Object> serializer) {
_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(_type, _serializer, type, serializer);
}
}
private final static class Double extends PropertySerializerMap
{
private final Class<?> _type1, _type2;
private final JsonSerializer<Object> _serializer1, _serializer2;
public Double(Class<?> type1, JsonSerializer<Object> serializer1,
Class<?> type2, JsonSerializer<Object> serializer2)
{
_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[2];
ts[0] = new TypeAndSerializer(_type1, _serializer1);
ts[1] = new TypeAndSerializer(_type2, _serializer2);
return new Multi(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.
*/
private final static int MAX_ENTRIES = 8;
private final TypeAndSerializer[] _entries;
public Multi(TypeAndSerializer[] entries) {
_entries = entries;
}
@Override
public JsonSerializer<Object> serializerFor(Class<?> type)
{
for (int i = 0, len = _entries.length; i < len; ++i) {
TypeAndSerializer entry = _entries[i];
if (entry.type == type) {
return entry.serializer;
}
}
return null;
}
@Override
public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer)
{
int len = _entries.length;
// Will only grow up to N entries
if (len == MAX_ENTRIES) {
return this;
}
// 1.6 has nice resize methods but we are still 1.5
TypeAndSerializer[] entries = new TypeAndSerializer[len+1];
System.arraycopy(_entries, 0, entries, 0, len);
entries[len] = new TypeAndSerializer(type, serializer);
return new Multi(entries);
}
}
}