From 57b287f3a1f56a69e94c31a6703c0ed0979d6984 Mon Sep 17 00:00:00 2001 From: Cowtowncoder Date: Thu, 2 Apr 2015 17:02:19 -0700 Subject: [PATCH] Start work on #688 --- .../databind/DeserializationConfig.java | 25 ++--- .../jackson/databind/ObjectMapper.java | 27 +++--- .../jackson/databind/SerializationConfig.java | 17 ++-- .../databind/cfg/MapperConfigBase.java | 48 ++++++---- .../introspect/ClassIntrospector.java | 10 ++ .../introspect/SimpleMixInResolver.java | 93 +++++++++++++++++++ 6 files changed, 164 insertions(+), 56 deletions(-) create mode 100644 src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java index f5b228110e..cbee948b31 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java @@ -3,24 +3,17 @@ import java.text.DateFormat; import java.util.*; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; + import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.cfg.BaseSettings; -import com.fasterxml.jackson.databind.cfg.ContextAttributes; -import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; -import com.fasterxml.jackson.databind.cfg.MapperConfigBase; + +import com.fasterxml.jackson.databind.cfg.*; import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler; -import com.fasterxml.jackson.databind.introspect.AnnotatedClass; -import com.fasterxml.jackson.databind.introspect.ClassIntrospector; -import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; -import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; -import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; -import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.jsontype.*; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.type.ClassKey; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.LinkedNode; @@ -80,7 +73,7 @@ public final class DeserializationConfig * Constructor used by ObjectMapper to create default configuration object instance. */ public DeserializationConfig(BaseSettings base, - SubtypeResolver str, Map> mixins) + SubtypeResolver str, SimpleMixInResolver mixins) { super(base, str, mixins); _deserFeatures = collectFeatureDefaults(DeserializationFeature.class); @@ -170,7 +163,7 @@ private DeserializationConfig(DeserializationConfig src, Class view) /** * @since 2.1 */ - protected DeserializationConfig(DeserializationConfig src, Map> mixins) + protected DeserializationConfig(DeserializationConfig src, SimpleMixInResolver mixins) { super(src, mixins); _deserFeatures = src._deserFeatures; diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index b0fa948e1d..4d3e815e20 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -272,8 +272,8 @@ public boolean useForType(JavaType t) * you can think of it as injecting annotations between the target * class and its sub-classes (or interfaces) */ - protected final HashMap> _mixInAnnotations; - + protected SimpleMixInResolver _mixInAnnotations; + /* /********************************************************** /* Configuration settings, serialization @@ -414,7 +414,7 @@ protected ObjectMapper(ObjectMapper src) _typeFactory = src._typeFactory; _injectableValues = src._injectableValues; - HashMap> mixins = new HashMap>(src._mixInAnnotations); + SimpleMixInResolver mixins = src._mixInAnnotations.copy(); _mixInAnnotations = mixins; _serializationConfig = new SerializationConfig(src._serializationConfig, mixins); _deserializationConfig = new DeserializationConfig(src._deserializationConfig, mixins); @@ -459,7 +459,7 @@ public ObjectMapper(JsonFactory jf, // and default type factory is shared one _typeFactory = TypeFactory.defaultInstance(); - HashMap> mixins = new HashMap>(); + SimpleMixInResolver mixins = new SimpleMixInResolver(null); _mixInAnnotations = mixins; BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector()); @@ -988,17 +988,16 @@ public SerializerProvider getSerializerProvider() { * Annotations from source classes (and their supertypes) * will override * annotations that target classes (and their super-types) have. + *

+ * Note that this method will CLEAR any previously defined mix-ins + * for this mapper. * * @since 2.5 */ public ObjectMapper setMixIns(Map, Class> sourceMixins) { - _mixInAnnotations.clear(); - if (sourceMixins != null && sourceMixins.size() > 0) { - for (Map.Entry,Class> en : sourceMixins.entrySet()) { - _mixInAnnotations.put(new ClassKey(en.getKey()), en.getValue()); - } - } + // NOTE: does NOT change possible externally configured resolver, just local defs + _mixInAnnotations.setLocalDefinitions(sourceMixins); return this; } @@ -1016,19 +1015,19 @@ public ObjectMapper setMixIns(Map, Class> sourceMixins) */ public ObjectMapper addMixIn(Class target, Class mixinSource) { - _mixInAnnotations.put(new ClassKey(target), mixinSource); + _mixInAnnotations.addLocalDefinition(target, mixinSource); return this; } public Class findMixInClassFor(Class cls) { - return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls)); + return _mixInAnnotations.findMixInClassFor(cls); } + // For testing only: public int mixInCount() { - return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size(); + return _mixInAnnotations.localSize(); } - /** * @deprecated Since 2.5: replaced by a fluent form of the method; {@link #setMixIns}. */ diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java index 6fad9f0c92..2099278e2d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java +++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java @@ -3,20 +3,21 @@ import java.text.DateFormat; import java.util.*; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; + import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.databind.cfg.BaseSettings; -import com.fasterxml.jackson.databind.cfg.ContextAttributes; -import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; -import com.fasterxml.jackson.databind.cfg.MapperConfigBase; + +import com.fasterxml.jackson.databind.cfg.*; import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.SerializerFactory; -import com.fasterxml.jackson.databind.type.ClassKey; import com.fasterxml.jackson.databind.type.TypeFactory; /** @@ -79,7 +80,7 @@ public final class SerializationConfig * Constructor used by ObjectMapper to create default configuration object instance. */ public SerializationConfig(BaseSettings base, - SubtypeResolver str, Map> mixins) + SubtypeResolver str, SimpleMixInResolver mixins) { super(base, str, mixins); _serFeatures = collectFeatureDefaults(SerializationFeature.class); @@ -163,7 +164,7 @@ private SerializationConfig(SerializationConfig src, String rootName) /** * @since 2.1 */ - protected SerializationConfig(SerializationConfig src, Map> mixins) + protected SerializationConfig(SerializationConfig src, SimpleMixInResolver mixins) { super(src, mixins); _serFeatures = src._serFeatures; diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java index b96298abb0..faaf98c3e0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java @@ -12,19 +12,19 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.introspect.ClassIntrospector; +import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; +import com.fasterxml.jackson.databind.introspect.SimpleMixInResolver; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; import com.fasterxml.jackson.databind.jsontype.SubtypeResolver; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; -import com.fasterxml.jackson.databind.type.ClassKey; import com.fasterxml.jackson.databind.type.TypeFactory; +@SuppressWarnings("serial") public abstract class MapperConfigBase> extends MapperConfig implements java.io.Serializable { - private static final long serialVersionUID = 6062961959359172474L; - private final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class); /* @@ -36,8 +36,10 @@ public abstract class MapperConfigBase> _mixInAnnotations; + protected final SimpleMixInResolver _mixIns; /** * Registered concrete subtypes that can be used instead of (or @@ -79,10 +81,10 @@ public abstract class MapperConfigBase> mixins) + SubtypeResolver str, SimpleMixInResolver mixins) { super(base, DEFAULT_MAPPER_FEATURES); - _mixInAnnotations = mixins; + _mixIns = mixins; _subtypeResolver = str; _rootName = null; _view = null; @@ -97,7 +99,7 @@ protected MapperConfigBase(BaseSettings base, protected MapperConfigBase(MapperConfigBase src) { super(src); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = src._view; @@ -107,7 +109,7 @@ protected MapperConfigBase(MapperConfigBase src) protected MapperConfigBase(MapperConfigBase src, BaseSettings base) { super(base, src._mapperFeatures); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = src._view; @@ -117,7 +119,7 @@ protected MapperConfigBase(MapperConfigBase src, BaseSettings base) protected MapperConfigBase(MapperConfigBase src, int mapperFeatures) { super(src._base, mapperFeatures); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = src._view; @@ -126,7 +128,7 @@ protected MapperConfigBase(MapperConfigBase src, int mapperFeatures) protected MapperConfigBase(MapperConfigBase src, SubtypeResolver str) { super(src); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = str; _rootName = src._rootName; _view = src._view; @@ -135,7 +137,7 @@ protected MapperConfigBase(MapperConfigBase src, SubtypeResolver str) { protected MapperConfigBase(MapperConfigBase src, String rootName) { super(src); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = rootName; _view = src._view; @@ -145,7 +147,7 @@ protected MapperConfigBase(MapperConfigBase src, String rootName) { protected MapperConfigBase(MapperConfigBase src, Class view) { super(src); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = view; @@ -155,10 +157,10 @@ protected MapperConfigBase(MapperConfigBase src, Class view) /** * @since 2.1 */ - protected MapperConfigBase(MapperConfigBase src, Map> mixins) + protected MapperConfigBase(MapperConfigBase src, SimpleMixInResolver mixins) { super(src); - _mixInAnnotations = mixins; + _mixIns = mixins; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = src._view; @@ -171,7 +173,7 @@ protected MapperConfigBase(MapperConfigBase src, Map> m protected MapperConfigBase(MapperConfigBase src, ContextAttributes attr) { super(src); - _mixInAnnotations = src._mixInAnnotations; + _mixIns = src._mixIns; _subtypeResolver = src._subtypeResolver; _rootName = src._rootName; _view = src._view; @@ -401,10 +403,20 @@ public final ContextAttributes getAttributes() { */ @Override public final Class findMixInClassFor(Class cls) { - return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls)); + return _mixIns.findMixInClassFor(cls); } + // Not really relevant here (should not get called) + @Override + public MixInResolver copy() { + throw new UnsupportedOperationException(); + } + + /** + * Test-only method -- does not reflect possibly open-ended set that external + * mix-in resolver might provide. + */ public final int mixInCount() { - return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size(); + return _mixIns.localSize(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java index 7ce48c5b71..9bd3f29472 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java @@ -33,6 +33,16 @@ public interface MixInResolver * annotations) for given class */ public Class findMixInClassFor(Class cls); + + /** + * Method called to create a new, non-shared copy, to be used by different + * ObjectMapper instance, and one that should not be connected + * to this instance, if resolver has mutable state. + * If resolver is immutable may simply return `this`. + * + * @since 2.6 + */ + public MixInResolver copy(); } protected ClassIntrospector() { } diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java new file mode 100644 index 0000000000..5d897c4306 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/SimpleMixInResolver.java @@ -0,0 +1,93 @@ +package com.fasterxml.jackson.databind.introspect; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * Simple implementation of {@link ClassIntrospector.MixInResolver} + * that just uses a {@link java.util.Map} for containing mapping + * from target to mix-in classes. + *

+ * Implementation is only thread-safe after initialization (that is, + * when underlying Map is not modified but only read). + * + * @since 2.6 + */ +public class SimpleMixInResolver + implements ClassIntrospector.MixInResolver, + java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * External resolver that gets called before looking at any locally defined + * mix-in target classes. + */ + protected final ClassIntrospector.MixInResolver _overrides; + + /** + * Simple mix-in targets defined locally. + */ + protected Map> _localMixIns; + + public SimpleMixInResolver(ClassIntrospector.MixInResolver overrides) { + _overrides = overrides; + } + + protected SimpleMixInResolver(ClassIntrospector.MixInResolver overrides, + Map> mixins) { + _overrides = overrides; + _localMixIns = mixins; + } + + protected SimpleMixInResolver withOverrides(ClassIntrospector.MixInResolver overrides) { + return new SimpleMixInResolver(overrides, _localMixIns); + } + + public SimpleMixInResolver withoutLocalDefinitions() { + return new SimpleMixInResolver(_overrides, null); + } + + public void setLocalDefinitions(Map, Class> sourceMixins) { + if (sourceMixins == null || sourceMixins.isEmpty()) { + _localMixIns = null; + } + Map> mixIns = new HashMap>(sourceMixins.size()); + for (Map.Entry,Class> en : sourceMixins.entrySet()) { + mixIns.put(new ClassKey(en.getKey()), en.getValue()); + } + _localMixIns = mixIns; + } + + public void addLocalDefinition(Class target, Class mixinSource) { + if (_localMixIns == null) { + _localMixIns = new HashMap>(); + } + _localMixIns.put(new ClassKey(target), mixinSource); + } + + @Override + public SimpleMixInResolver copy() { + ClassIntrospector.MixInResolver overrides = (_overrides == null) + ? null : _overrides.copy(); + Map> mixIns = (_localMixIns == null) + ? null : new HashMap>(_localMixIns); + return new SimpleMixInResolver(overrides, mixIns); + } + + @Override + public Class findMixInClassFor(Class cls) + { + Class mixin = (_overrides == null) ? null : _overrides.findMixInClassFor(cls); + if (mixin == null && (_localMixIns != null)) { + mixin = _localMixIns.get(new ClassKey(cls)); + } + return mixin; + } + + public int localSize() { + return (_localMixIns == null) ? 0 : _localMixIns.size(); + } +}