| package org.codehaus.jackson.map.ser; |
| |
| import java.lang.reflect.Modifier; |
| import java.util.*; |
| |
| import org.codehaus.jackson.map.*; |
| import org.codehaus.jackson.map.type.ClassKey; |
| import org.codehaus.jackson.type.JavaType; |
| |
| /** |
| * Serializer factory implementation that allows for configuring |
| * mapping between types (classes) and serializers to use, by using |
| * multiple types of overrides. Existing mappings established by |
| * {@link BeanSerializerFactory} (and its super class, |
| * {@link BasicSerializerFactory}) are used if no overrides are |
| * defined. |
| *<p> |
| * Unlike base serializer factories ({@link BasicSerializerFactory}, |
| * {@link BeanSerializerFactory}), this factory is stateful because |
| * of configuration settings. It is thread-safe, however, as long as |
| * all configuration as done before using the factory -- a single |
| * instance can be shared between providers and mappers. |
| *<p> |
| * Configurations currently available are: |
| *<ul> |
| * <li>Ability to define explicit mappings between classes and interfaces |
| * and serializers to use. These can be either specific ones (class must |
| * match exactly) or generic ones (any sub-class or class implementing |
| * the interface); specific ones have precedence over generic ones (and |
| * precedence between generic ones is not defined). |
| * </li> |
| * <li>Ability to define a single generic base serializer for all Enum |
| * types (precedence below specific serializer mapping) |
| * </li> |
| *</ul> |
| *<p> |
| * Note: as of version 1.7, this class is not as useful as it used to |
| * be, due to addition of "modules", which allow simpler addition |
| * of custom serializers and deserializers. In fact, use of module system |
| * is recommended even when not exposing serializers or deserializers |
| * as a pluggable library (just using them locally). |
| */ |
| public class CustomSerializerFactory |
| extends BeanSerializerFactory |
| { |
| /* |
| /********************************************************** |
| /* Configuration, direct/special mappings |
| /********************************************************** |
| */ |
| |
| /** |
| * Direct mappings that are only used for exact class type |
| * matches, but not for sub-class checks. |
| */ |
| protected HashMap<ClassKey,JsonSerializer<?>> _directClassMappings = null; |
| |
| /** |
| * And for Enum handling we may specify a single default |
| * serializer to use, regardless of actual enumeration. |
| * Usually used to provide "toString - serializer". |
| */ |
| protected JsonSerializer<?> _enumSerializerOverride; |
| |
| /* |
| /********************************************************** |
| /* Configuration, generic (interface, super-class) mappings |
| /********************************************************** |
| */ |
| |
| /** |
| * And then class-based mappings that are used both for exact and |
| * sub-class matches. |
| */ |
| protected HashMap<ClassKey,JsonSerializer<?>> _transitiveClassMappings = null; |
| |
| /** |
| * And finally interface-based matches. |
| */ |
| protected HashMap<ClassKey,JsonSerializer<?>> _interfaceMappings = null; |
| |
| /* |
| /********************************************************** |
| /* Life-cycle, constructors |
| /********************************************************** |
| */ |
| |
| public CustomSerializerFactory() { |
| this(null); |
| } |
| |
| public CustomSerializerFactory(Config config) { |
| super(config); |
| } |
| |
| @Override |
| public SerializerFactory withConfig(Config config) |
| { |
| /* 22-Nov-2010, tatu: As with BeanSerializerFactory, must ensure type won't change |
| * with this method, so: |
| */ |
| if (getClass() != CustomSerializerFactory.class) { |
| throw new IllegalStateException("Subtype of CustomSerializerFactory ("+getClass().getName() |
| +") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with " |
| +"additional serializer definitions"); |
| } |
| return new CustomSerializerFactory(config); |
| } |
| |
| /* |
| /********************************************************** |
| /* Configuration: type-to-serializer mappings |
| /********************************************************** |
| */ |
| |
| /** |
| * Method used to add a generic (transitive) mapping from specified |
| * class or its sub-classes into a serializer. |
| * When resolving a type into a serializer, explicit class is checked |
| * first, then immediate super-class, and so forth along inheritance |
| * chain. But if this fails, implemented interfaces are checked; |
| * ordering is done such that first interfaces implemented by |
| * the exact type are checked (in order returned by |
| * {@link Class#getInterfaces}), then super-type's and so forth. |
| *<p> |
| * Note that adding generic mappings may lead to problems with |
| * sub-classing: if sub-classes add new properties, these may not |
| * get properly serialized. |
| * |
| * @param type Class for which specified serializer is to be |
| * used. May be more specific type than what serializer indicates, |
| * but must be compatible (same or sub-class) |
| */ |
| public <T> void addGenericMapping(Class<? extends T> type, JsonSerializer<T> ser) |
| { |
| // Interface to match? |
| ClassKey key = new ClassKey(type); |
| if (type.isInterface()) { |
| if (_interfaceMappings == null) { |
| _interfaceMappings = new HashMap<ClassKey,JsonSerializer<?>>(); |
| } |
| _interfaceMappings.put(key, ser); |
| } else { // nope, class: |
| if (_transitiveClassMappings == null) { |
| _transitiveClassMappings = new HashMap<ClassKey,JsonSerializer<?>>(); |
| } |
| _transitiveClassMappings.put(key, ser); |
| } |
| } |
| |
| /** |
| * Method used to add a mapping from specific type -- and only that |
| * type -- to specified serializer. This means that binding is not |
| * used for sub-types. It also means that no such mappings are to |
| * be defined for abstract classes or interfaces: and if an attempt |
| * is made, {@link IllegalArgumentException} will be thrown to |
| * indicate caller error. |
| * |
| * @param forClass Class for which specified serializer is to be |
| * used. May be more specific type than what serializer indicates, |
| * but must be compatible (same or sub-class) |
| */ |
| public <T> void addSpecificMapping(Class<? extends T> forClass, JsonSerializer<T> ser) |
| { |
| ClassKey key = new ClassKey(forClass); |
| |
| /* First, let's ensure it's not an interface or abstract class: |
| * as those can not be instantiated, such mappings would never |
| * get used. |
| */ |
| if (forClass.isInterface()) { |
| throw new IllegalArgumentException("Can not add specific mapping for an interface ("+forClass.getName()+")"); |
| } |
| if (Modifier.isAbstract(forClass.getModifiers())) { |
| throw new IllegalArgumentException("Can not add specific mapping for an abstract class ("+forClass.getName()+")"); |
| } |
| |
| if (_directClassMappings == null) { |
| _directClassMappings = new HashMap<ClassKey,JsonSerializer<?>>(); |
| } |
| _directClassMappings.put(key, ser); |
| } |
| |
| /** |
| * Method that can be used to force specified serializer to be used for |
| * serializing all Enum instances. This is most commonly used to specify |
| * serializers that call either <code>enum.toString()</code>, or modify |
| * value returned by <code>enum.name()</code> (such as upper- or |
| * lower-casing it). |
| *<p> |
| * Note: this serializer has lower precedence than that of specific |
| * types; so if a specific serializer is assigned to an Enum type, |
| * this serializer will NOT be used. It has higher precedence than |
| * generic mappings have however. |
| */ |
| public void setEnumSerializer(JsonSerializer<?> enumSer) |
| { |
| _enumSerializerOverride = enumSer; |
| } |
| |
| /* |
| /********************************************************** |
| /* JsonSerializerFactory impl |
| /********************************************************** |
| */ |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public JsonSerializer<Object> createSerializer(SerializationConfig config, JavaType type, |
| BeanProperty property) |
| throws JsonMappingException |
| { |
| JsonSerializer<?> ser = findCustomSerializer(type.getRawClass(), config); |
| if (ser != null) { |
| return (JsonSerializer<Object>) ser; |
| } |
| return super.createSerializer(config, type, property); |
| } |
| |
| /* |
| /********************************************************** |
| /* Internal methods |
| /********************************************************** |
| */ |
| |
| protected JsonSerializer<?> findCustomSerializer(Class<?> type, SerializationConfig config) |
| { |
| JsonSerializer<?> ser = null; |
| ClassKey key = new ClassKey(type); |
| |
| // First: exact matches |
| if (_directClassMappings != null) { |
| ser = _directClassMappings.get(key); |
| if (ser != null) { |
| return ser; |
| } |
| } |
| |
| // No match? Perhaps we can use the enum serializer? |
| if (type.isEnum()) { |
| if (_enumSerializerOverride != null) { |
| return _enumSerializerOverride; |
| } |
| } |
| |
| // Still no match? How about more generic ones? |
| // Mappings for super-classes? |
| if (_transitiveClassMappings != null) { |
| for (Class<?> curr = type; (curr != null); curr = curr.getSuperclass()) { |
| key.reset(curr); |
| ser = _transitiveClassMappings.get(key); |
| if (ser != null) { |
| return ser; |
| } |
| } |
| } |
| |
| // And if still no match, how about interfaces? |
| if (_interfaceMappings != null) { |
| // as per [JACKSON-327], better check actual interface first too... |
| key.reset(type); |
| ser = _interfaceMappings.get(key); |
| if (ser != null) { |
| return ser; |
| } |
| for (Class<?> curr = type; (curr != null); curr = curr.getSuperclass()) { |
| ser = _findInterfaceMapping(curr, key); |
| if (ser != null) { |
| return ser; |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected JsonSerializer<?> _findInterfaceMapping(Class<?> cls, ClassKey key) |
| { |
| for (Class<?> iface : cls.getInterfaces()) { |
| key.reset(iface); |
| JsonSerializer<?> ser = _interfaceMappings.get(key); |
| if (ser != null) { |
| return ser; |
| } |
| // [JACKSON-373] need to also check super-interfaces |
| ser = _findInterfaceMapping(iface, key); |
| if (ser != null) { |
| return ser; |
| } |
| } |
| return null; |
| } |
| } |
| |