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. */ 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 */ protected final boolean _resetWhenFull; /** * @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 can not 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. * * @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. * * @since 2.3 * * @throws JsonMappingException */ public final SerializerAndMapResult findAndAddSecondarySerializer(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 findAndAddSecondarySerializer(JavaType type, SerializerProvider provider, BeanProperty property) throws JsonMappingException { JsonSerializer<Object> serializer = provider.findValueSerializer(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 {@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 */ 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 */ 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 */ public final SerializerAndMapResult addSerializer(Class<?> type, JsonSerializer<Object> serializer) { return new SerializerAndMapResult(serializer, newWith(type, serializer)); } /** * @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 {@link #emptyForProperties} instead */ @Deprecated public static PropertySerializerMap emptyMap() { return emptyForProperties(); } /** * @since 2.5 */ public static PropertySerializerMap emptyForProperties() { return Empty.FOR_PROPERTIES; } /** * @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 */ 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 { // 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. */ 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. */ 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) { 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. 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); } } }