DeserializationConfig.java
package tools.jackson.databind;
import tools.jackson.core.*;
import tools.jackson.databind.cfg.*;
import tools.jackson.databind.deser.DeserializationProblemHandler;
import tools.jackson.databind.introspect.*;
import tools.jackson.databind.jsontype.*;
import tools.jackson.databind.type.LogicalType;
import tools.jackson.databind.type.TypeFactory;
import tools.jackson.databind.util.ArrayIterator;
import tools.jackson.databind.util.Converter;
import tools.jackson.databind.util.LinkedNode;
import tools.jackson.databind.util.RootNameLookup;
/**
* Object that contains baseline configuration for deserialization
* process. An instance is owned by {@link ObjectMapper}, which
* passes an immutable instance to be used for deserialization process.
*<p>
* Note that instances are considered immutable and as such no copies
* should need to be created for sharing; all copying is done with
* "fluent factory" methods.
*/
public final class DeserializationConfig
extends MapperConfigBase<DeserializationFeature, DeserializationConfig>
implements java.io.Serializable
{
private static final long serialVersionUID = 3L;
/*
/**********************************************************************
/* Deserialization, parser, format features
/**********************************************************************
*/
/**
* Set of {@link DeserializationFeature}s enabled.
*/
protected final int _deserFeatures;
/**
* States of {@link tools.jackson.core.StreamReadFeature}s to enable/disable.
*/
protected final int _streamReadFeatures;
/**
* States of {@link tools.jackson.core.FormatFeature}s to enable/disable.
*/
protected final int _formatReadFeatures;
/*
/**********************************************************************
/* Configured helper objects
/**********************************************************************
*/
/**
* Linked list that contains all registered problem handlers.
* Implementation as front-added linked list allows for sharing
* of the list (tail) without copying the list.
*/
protected final LinkedNode<DeserializationProblemHandler> _problemHandlers;
/**
* List of objects that may be able to resolve abstract types to
* concrete types. Used by functionality like "mr Bean" to materialize
* types as needed, although may be used for other kinds of defaulting
* as well.
*/
protected final AbstractTypeResolver[] _abstractTypeResolvers;
/**
* Configured coercion rules for coercions from secondary input
* shapes.
*/
protected final CoercionConfigs _coercionConfigs;
/*
/**********************************************************************
/* Life-cycle, primary constructors for new instances
/**********************************************************************
*/
/**
* @since 3.0
*/
public DeserializationConfig(MapperBuilder<?,?> b, long mapperFeatures,
int deserFeatures, int streamReadFeatures, int formatReadFeatures,
ConfigOverrides configOverrides, CoercionConfigs coercionConfigs,
TypeFactory tf, ClassIntrospector classIntr, MixInHandler mixins, SubtypeResolver str,
ContextAttributes defaultAttrs, RootNameLookup rootNames,
AbstractTypeResolver[] atrs)
{
super(b, mapperFeatures, tf, classIntr, mixins, str, configOverrides,
defaultAttrs, rootNames);
_deserFeatures = deserFeatures;
_streamReadFeatures = streamReadFeatures;
_formatReadFeatures = formatReadFeatures;
_problemHandlers = b.deserializationProblemHandlers();
_coercionConfigs = coercionConfigs;
_abstractTypeResolvers = atrs;
}
/*
/**********************************************************************
/* Life-cycle, secondary constructors to support
/* "mutant factories", with single property changes
/**********************************************************************
*/
private DeserializationConfig(DeserializationConfig src,
int deserFeatures, int streamReadFeatures, int formatReadFeatures)
{
super(src);
_deserFeatures = deserFeatures;
_streamReadFeatures = streamReadFeatures;
_formatReadFeatures = formatReadFeatures;
_coercionConfigs = src._coercionConfigs;
_problemHandlers = src._problemHandlers;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
private DeserializationConfig(DeserializationConfig src, BaseSettings base)
{
super(src, base);
_deserFeatures = src._deserFeatures;
_streamReadFeatures = src._streamReadFeatures;
_formatReadFeatures = src._formatReadFeatures;
_coercionConfigs = src._coercionConfigs;
_problemHandlers = src._problemHandlers;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
private DeserializationConfig(DeserializationConfig src,
LinkedNode<DeserializationProblemHandler> problemHandlers,
AbstractTypeResolver[] atr)
{
super(src);
_deserFeatures = src._deserFeatures;
_streamReadFeatures = src._streamReadFeatures;
_formatReadFeatures = src._formatReadFeatures;
_coercionConfigs = src._coercionConfigs;
_problemHandlers = problemHandlers;
_abstractTypeResolvers = atr;
}
private DeserializationConfig(DeserializationConfig src, PropertyName rootName)
{
super(src, rootName);
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_streamReadFeatures = src._streamReadFeatures;
_coercionConfigs = src._coercionConfigs;
_formatReadFeatures = src._formatReadFeatures;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
private DeserializationConfig(DeserializationConfig src, Class<?> view)
{
super(src, view);
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_streamReadFeatures = src._streamReadFeatures;
_coercionConfigs = src._coercionConfigs;
_formatReadFeatures = src._formatReadFeatures;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
protected DeserializationConfig(DeserializationConfig src, ContextAttributes attrs)
{
super(src, attrs);
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_coercionConfigs = src._coercionConfigs;
_streamReadFeatures = src._streamReadFeatures;
_formatReadFeatures = src._formatReadFeatures;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
protected DeserializationConfig(DeserializationConfig src, DatatypeFeatures dtFeatures)
{
super(src, dtFeatures);
_deserFeatures = src._deserFeatures;
_problemHandlers = src._problemHandlers;
_coercionConfigs = src._coercionConfigs;
_streamReadFeatures = src._streamReadFeatures;
_formatReadFeatures = src._formatReadFeatures;
_abstractTypeResolvers = src._abstractTypeResolvers;
}
// for unit tests only:
protected BaseSettings getBaseSettings() { return _base; }
/*
/**********************************************************************
/* Life-cycle, general factory methods from MapperConfig(Base)
/**********************************************************************
*/
@Override
protected final DeserializationConfig _withBase(BaseSettings newBase) {
return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
}
@Override
protected final DeserializationConfig _with(DatatypeFeatures dtFeatures) {
return new DeserializationConfig(this, dtFeatures);
}
/*
/**********************************************************************
/* Life-cycle, specific factory methods from MapperConfig
/**********************************************************************
*/
@Override
public DeserializationConfig withRootName(PropertyName rootName) {
if (rootName == null) {
if (_rootName == null) {
return this;
}
} else if (rootName.equals(_rootName)) {
return this;
}
return new DeserializationConfig(this, rootName);
}
@Override
public DeserializationConfig withView(Class<?> view) {
return (_view == view) ? this : new DeserializationConfig(this, view);
}
@Override
public DeserializationConfig with(ContextAttributes attrs) {
return (attrs == _attributes) ? this : new DeserializationConfig(this, attrs);
}
/*
/**********************************************************************
/* Life-cycle, DeserializationFeature-based factory methods
/**********************************************************************
*/
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig with(DeserializationFeature feature)
{
int newDeserFeatures = (_deserFeatures | feature.getMask());
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures,
_formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig with(DeserializationFeature first,
DeserializationFeature... features)
{
int newDeserFeatures = _deserFeatures | first.getMask();
for (DeserializationFeature f : features) {
newDeserFeatures |= f.getMask();
}
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures,
_formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig withFeatures(DeserializationFeature... features)
{
int newDeserFeatures = _deserFeatures;
for (DeserializationFeature f : features) {
newDeserFeatures |= f.getMask();
}
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this, newDeserFeatures,
_streamReadFeatures, _formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified feature disabled.
*/
public DeserializationConfig without(DeserializationFeature feature)
{
int newDeserFeatures = _deserFeatures & ~feature.getMask();
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this, newDeserFeatures,
_streamReadFeatures, _formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features disabled.
*/
public DeserializationConfig without(DeserializationFeature first,
DeserializationFeature... features)
{
int newDeserFeatures = _deserFeatures & ~first.getMask();
for (DeserializationFeature f : features) {
newDeserFeatures &= ~f.getMask();
}
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this, newDeserFeatures, _streamReadFeatures,
_formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features disabled.
*/
public DeserializationConfig withoutFeatures(DeserializationFeature... features)
{
int newDeserFeatures = _deserFeatures;
for (DeserializationFeature f : features) {
newDeserFeatures &= ~f.getMask();
}
return (newDeserFeatures == _deserFeatures) ? this :
new DeserializationConfig(this,
newDeserFeatures, _streamReadFeatures, _formatReadFeatures);
}
/*
/**********************************************************************
/* Life-cycle, JsonParser.Feature-based factory methods
/**********************************************************************
*/
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig with(StreamReadFeature feature)
{
int newSet = _streamReadFeatures | feature.getMask();
return (_streamReadFeatures == newSet)? this :
new DeserializationConfig(this,
_deserFeatures, newSet, _formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig withFeatures(StreamReadFeature... features)
{
int newSet = _streamReadFeatures;
for (StreamReadFeature f : features) {
newSet |= f.getMask();
}
return (_streamReadFeatures == newSet) ? this :
new DeserializationConfig(this, _deserFeatures, newSet,
_formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified feature disabled.
*/
public DeserializationConfig without(StreamReadFeature feature)
{
int newSet = _streamReadFeatures & ~feature.getMask();
return (_streamReadFeatures == newSet) ? this :
new DeserializationConfig(this, _deserFeatures, newSet,
_formatReadFeatures);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features disabled.
*/
public DeserializationConfig withoutFeatures(StreamReadFeature... features)
{
int newSet = _streamReadFeatures;
for (StreamReadFeature f : features) {
newSet &= ~f.getMask();
}
return (_streamReadFeatures == newSet)? this :
new DeserializationConfig(this, _deserFeatures, newSet, _formatReadFeatures);
}
/*
/**********************************************************************
/* Life-cycle, JsonParser.FormatFeature-based factory methods
/**********************************************************************
*/
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig with(FormatFeature feature)
{
int newSet = _formatReadFeatures | feature.getMask();
return (_formatReadFeatures == newSet) ? this
: new DeserializationConfig(this,
_deserFeatures, _streamReadFeatures, newSet);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features enabled.
*/
public DeserializationConfig withFeatures(FormatFeature... features)
{
int newSet = _formatReadFeatures;
for (FormatFeature f : features) {
newSet |= f.getMask();
}
return (_formatReadFeatures == newSet) ? this
: new DeserializationConfig(this,
_deserFeatures, _streamReadFeatures, newSet);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified feature disabled.
*/
public DeserializationConfig without(FormatFeature feature)
{
int newSet = _formatReadFeatures & ~feature.getMask();
return (_formatReadFeatures == newSet) ? this
: new DeserializationConfig(this,
_deserFeatures, _streamReadFeatures, newSet);
}
/**
* Fluent factory method that will construct and return a new configuration
* object instance with specified features disabled.
*/
public DeserializationConfig withoutFeatures(FormatFeature... features)
{
int newSet = _formatReadFeatures;
for (FormatFeature f : features) {
newSet &= ~f.getMask();
}
return (_formatReadFeatures == newSet) ? this
: new DeserializationConfig(this,
_deserFeatures, _streamReadFeatures, newSet);
}
/*
/**********************************************************************
/* Life-cycle, deserialization-specific factory methods
/**********************************************************************
*/
/**
* Method that can be used to add a handler that can (try to)
* resolve non-fatal deserialization problems.
*/
public DeserializationConfig withHandler(DeserializationProblemHandler h)
{
// Sanity check: let's prevent adding same handler multiple times
return LinkedNode.contains(_problemHandlers, h) ? this
: new DeserializationConfig(this,
new LinkedNode<DeserializationProblemHandler>(h, _problemHandlers),
_abstractTypeResolvers);
}
/**
* Method for removing all configured problem handlers; usually done to replace
* existing handler(s) with different one(s)
*/
public DeserializationConfig withNoProblemHandlers() {
return (_problemHandlers == null) ? this
: new DeserializationConfig(this,
(LinkedNode<DeserializationProblemHandler>) null,
_abstractTypeResolvers);
}
/*
/**********************************************************************
/* Support for ObjectReadContext
/**********************************************************************
*/
/**
* @since 3.0
*/
public int getStreamReadFeatures() {
return _streamReadFeatures;
}
/**
* @since 3.0
*/
public int getFormatReadFeatures() {
return _formatReadFeatures;
}
/*
/**********************************************************************
/* MapperConfig implementation/overrides: other
/**********************************************************************
*/
@Override
public boolean useRootWrapping()
{
if (_rootName != null) { // empty String disables wrapping; non-empty enables
return !_rootName.isEmpty();
}
return isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE);
}
public final boolean isEnabled(DeserializationFeature f) {
return (_deserFeatures & f.getMask()) != 0;
}
public final boolean isEnabled(StreamReadFeature f) {
return (_streamReadFeatures & f.getMask()) != 0;
}
public final boolean hasFormatFeature(FormatFeature f) {
return (_formatReadFeatures & f.getMask()) != 0;
}
/**
* Bulk access method for checking that all features specified by
* mask are enabled.
*/
public final boolean hasDeserializationFeatures(int featureMask) {
return (_deserFeatures & featureMask) == featureMask;
}
/**
* Bulk access method for checking that at least one of features specified by
* mask is enabled.
*/
public final boolean hasSomeOfFeatures(int featureMask) {
return (_deserFeatures & featureMask) != 0;
}
/**
* Bulk access method for getting the bit mask of all {@link DeserializationFeature}s
* that are enabled.
*/
public final int getDeserializationFeatures() {
return _deserFeatures;
}
/**
* Convenience method equivalent to:
*<code>
* isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
*</code>
*/
public final boolean requiresFullValue() {
return DeserializationFeature.FAIL_ON_TRAILING_TOKENS.enabledIn(_deserFeatures);
}
/*
/**********************************************************************
/* Abstract type mapping
/**********************************************************************
*/
/**
* @since 3.0
*/
public boolean hasAbstractTypeResolvers() { return _abstractTypeResolvers.length > 0; }
/**
* @since 3.0
*/
public Iterable<AbstractTypeResolver> abstractTypeResolvers() {
return new ArrayIterator<AbstractTypeResolver>(_abstractTypeResolvers);
}
/**
* @since 3.0
*/
public JavaType mapAbstractType(JavaType type)
{
if (!hasAbstractTypeResolvers()) {
return type;
}
// first, general mappings
while (true) {
JavaType next = _mapAbstractType2(type);
if (next == null) {
return type;
}
// Should not have to worry about cycles; but better verify since they will invariably occur... :-)
// (also: guard against invalid resolution to a non-related type)
Class<?> prevCls = type.getRawClass();
Class<?> nextCls = next.getRawClass();
if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) {
throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former");
}
type = next;
}
}
/**
* Method that will find abstract type mapping for specified type, doing a single
* lookup through registered abstract type resolvers; will not do recursive lookups.
*/
private JavaType _mapAbstractType2(JavaType type)
{
Class<?> currClass = type.getRawClass();
for (AbstractTypeResolver resolver : abstractTypeResolvers()) {
JavaType concrete = resolver.findTypeMapping(this, type);
if ((concrete != null) && !concrete.hasRawClass(currClass)) {
return concrete;
}
}
return null;
}
/*
/**********************************************************************
/* Other configuration
/**********************************************************************
*/
/**
* Method for getting head of the problem handler chain. May be null,
* if no handlers have been added.
*/
public LinkedNode<DeserializationProblemHandler> getProblemHandlers() {
return _problemHandlers;
}
/*
/**********************************************************************
/* CoercionConfig access
/**********************************************************************
*/
/**
* General-purpose accessor for finding what to do when specified coercion
* from shape that is now always allowed to be coerced from is requested.
*
* @param targetType Logical target type of coercion
* @param targetClass Physical target type of coercion
* @param inputShape Input shape to coerce from
*
* @return CoercionAction configured for specific coercion
*
* @since 2.12
*/
public CoercionAction findCoercionAction(LogicalType targetType,
Class<?> targetClass, CoercionInputShape inputShape)
{
return _coercionConfigs.findCoercion(this,
targetType, targetClass, inputShape);
}
/**
* More specialized accessor called in case of input being a blank
* String (one consisting of only white space characters with length of at least one).
* Will basically first determine if "blank as empty" is allowed: if not,
* returns {@code actionIfBlankNotAllowed}, otherwise returns action for
* {@link CoercionInputShape#EmptyString}.
*
* @param targetType Logical target type of coercion
* @param targetClass Physical target type of coercion
* @param actionIfBlankNotAllowed Return value to use in case "blanks as empty"
* is not allowed
*
* @return CoercionAction configured for specified coercion from blank string
*
* @since 2.12
*/
public CoercionAction findCoercionFromBlankString(LogicalType targetType,
Class<?> targetClass,
CoercionAction actionIfBlankNotAllowed)
{
return _coercionConfigs.findCoercionFromBlankString(this,
targetType, targetClass, actionIfBlankNotAllowed);
}
/*
/**********************************************************************
/* Introspection support
/**********************************************************************
*/
// @since 3.0
public Converter<Object,Object> findDeserializationConverter(Annotated ann)
{
return _createConverter(ann,
getAnnotationIntrospector().findDeserializationConverter(this, ann));
}
}